1

I'm learning about internal iterators and I understand that an internal iterator manage the iterations in the background:

   public void internalIterator(){
      List<String> namesList = Arrays.asList("Tom", "Dick", "Harry");
      namesList.forEach(name -> System.out.println(name));
   }

But I think that enhanced for loop does the same thing:

 public void enhancedForLoop(){
  List<String> namesList = Arrays.asList("Tom", "Dick", "Harry");
  for(String name : namesList){
    System.out.println(name);
  }
 }

I know that enhanced for loop uses hasNext() and next() methods in the background. And enhanced for loop is an external iterator. Then why forEach() method is an internal iterator? Doesn't forEach() method use hasNext() and next() methods in the background? How the iterations are managed in the backgorund in a different way than enhanced for loop? And is the iteration faster using forEach() than using enhanced for loop? Any feedback will be apreciated?

elvis
  • 956
  • 9
  • 33
  • 56
  • The implementation of `forEach` depends on the type of the collection. For example, `ArrayList` uses a standard indexed loop. `LinkedList` inherits the default implementation of `Iterable`, which uses an enhanced for loop. Enhanced for loops use iterators to traverse the collection, so the answer is: it depends on the collection you use. – Vince Aug 26 '20 at 13:20
  • @Dioxin Do you have a source for “ArrayList uses a standard indexed loop”? The final operand of a for-each loop is required to be an Iterable, which implies that a Iterable.iterator() is always used. – VGR Aug 26 '20 at 14:55
  • @VGR What do you want to say about "final operand of a for-each loop is required to be an Iterable"? What final operand has the for-each loop? Do you have a link? Thank you! – elvis Aug 27 '20 at 09:25
  • @VGR You can check the source code. Here's a link to OpenJDK's `ArrayList#forEach`: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L1505-L1514 – Vince Aug 27 '20 at 10:30
  • @VGR `iterator()` is not always used. One of the goals behind introducing `forEach` was allowing implementation-specific iteration. `ArrayList` is still `Iterable`. But the devs decided it's more optimal to use indexed looping instead of the list's iterator. `ArrayDeque` also provides it's own `forEach` implementation which doesn't use `iterator()`. As for enhanced-loops, `Iterable` is only one requirement (JLS 14.14.2); when an array is used in an enhanced loop, it translates to an indexed loop. – Vince Aug 27 '20 at 11:04

3 Answers3

2

See also the definition of forEach on the interface Iterable. Both constructs use different methods of the same interface.

Conceptually, the difference is that in an "enhanced for loop" the class implementing the Iterable only creates the Iterator, and the loop construct is responsible for advancing it (see also this related question).

When calling forEach, the class controls the entire process of iteration, and can pick whatever is most suitable for its underlying data structure. For example, it could avoid creating an Iterator object, and instead use some internal array index or similar.

Other than that, they should be equivalent. In most cases I wouldn't expect any difference in performance.

See this excellent answer for some additional reasons why you might want to use forEach, e.g. additional consistency guarantees when iterating synchronized collections, and this one mentioning things that it does not provide, such as flow control (e.g. short-circuiting using break) or support for checked exceptions.

Hulk
  • 6,399
  • 1
  • 30
  • 52
  • Thank you for response! Does the enhanced for loop always use the Iterator or it could use index counter? I'm looking at the link that you posted and the best answer said: "if the right-hand side of the for (:) idiom is an array rather than an Iterable object, the internal code uses an int index counter and checks against array.length instead" – elvis Aug 27 '20 at 07:53
  • 1
    Yes, these two cases are explicitly covered in the [JLS 14.14.2. The enhanced for statement](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2). For arrays it uses indices, for `Iterables` it uses `iterator()`, other types are not supported. – Hulk Aug 27 '20 at 08:47
  • So, "enhanced for loop" use indices for arrays and iterator() for Iterables. And forEach() method isn't for arrays it is only for Iterables, but for Iterables it could use iterator() or indices depending on the collection that it use. Right? – elvis Aug 27 '20 at 09:18
1

The difference is what the user of the API has to do.


Differences

External

When using iterator(), it's the user's job (your job) to manage the traversal: when to call hasNext() and when to call next(). The iteration is handled externally from the API, by the user of the API. The user both iterates through the elements & consumes the elements.

Internal

When using forEach, it's the API's job to manage the traversal. The iteration is handled internally instead of by the user; the user only consumes the elements.


Conclusion

It doesn't matter if hasNext() and next() are being called. What does matter is who calls hasNext() and next() - who handles the iteration, who is in charge of the iterator.

forEach is internal because the user doesn't have control over how the elements are iterated through. The API handles it; the iterator is internal.

iterator() is external because the user must define how iteration will occur. The API passes you the iterator, and that's all the API does. The iterator is external.

Even though forEach uses iterator() in some cases, that iterator is still internal to the API you're using (the List). Whoever calls forEach still only worry about consuming elements. The user doesn't control how the elements are traversed through, so it doesn't matter what forEach uses.


Suggestions on when to use a strategy

You'd use an enhanced loop when you need the most basic of sequential iteration.

You'd use an iterator() when you need more complex iteration.

You'd use forEach when you're only worried about consuming the elements, and don't mind if the API decides on how to traverse through the collection.

Vince
  • 14,470
  • 7
  • 39
  • 84
  • So when you use iterator() is external and when you use forEach is external. What about enhanced for loop? – elvis Aug 27 '20 at 13:11
  • 1
    @elvis `forEach` is internal. Using an enhanced for loop is no different than using an `iterator()` yourself. The enhanced for loop does is make it so you don't have to write `for (Iterator i = namesList.iterator(); i.hasNext(); ) { String name = i.next(); ... }` (if using an `Iterable`). But it works the exact same as if you were to write all that code yourself. That's why it's considered "syntactic sugar". So using an enhanced for loop would make use of an external iterator. – Vince Aug 27 '20 at 13:18
  • Sorry, I wanted to say forEach is internal. – elvis Aug 27 '20 at 13:28
-1

I don't know where you got that "enhanced for loop is an external iterator", but Iterable.forEach just does the same thing as your second example (could be overridden, though I don't see any reason to).

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
Michael
  • 41,989
  • 11
  • 82
  • 128
  • The enhanced for-loop is an external iterator whereas the new forEach method is an internal one: https://www.baeldung.com/foreach-java – elvis Aug 26 '20 at 13:54
  • OK, I had not heard that definition either yet, but ok. Btw. I found another related question, but beware the answers there are somewhat opinionated (still some good discussion of the differences, although it mixes in quite a bit of semi-related Stream API discussions): https://stackoverflow.com/questions/16635398/java-8-iterable-foreach-vs-foreach-loop?rq=1 – Hulk Aug 26 '20 at 14:03
  • @elvis It looks like the author is trying to get philosophical. I think they are trying to assert some huge difference as one being declarative and the other imperative. I don't know how they can call enhanced for-loop an "external" iterator, when the whole presence of the iterator is hidden. I would ignore that article, seems stupid to me. – Michael Aug 26 '20 at 14:05
  • @Michael The terms have been used long before that article was written. Here's an example on Stack Overflow from 2008: https://stackoverflow.com/questions/224648/external-iterator-vs-internal-iterator – Vince Aug 27 '20 at 10:52
  • @Dioxin That doesn't mean enhanced for-loop is an external iterator. The article says, in reference to an enhance for-loop "*the complexity of these operations is hidden from the programmer but it still exists*". Well, that is exactly the case with `forEach` as well, as you can see from the snippet above. – Michael Aug 27 '20 at 11:06
  • @Michael I wasn't saying you were wrong that it can be seen as imperative vs declarative. But any form of an iterator that is managed by the client of the API (calling `hasNext` and `next`) is an external iterator. Enhanced for loops are just syntactic sugar, mentioned in [14.14.2](https://docs.oracle.com/javase/specs/jls/se14/html/jls-14.html#jls-14.14.2) of the JLS; the API is still exposing an iterator in that case, which the client must control the iteration. The language just hides it for you in cases of the most basic iteration. – Vince Aug 27 '20 at 11:12
  • @Dioxin The `forEach` method belongs to `Iterable`. What is that interface doing if not "exposing an iterator"? You will not be able to convince me that's there's a difference, because there isn't one. – Michael Aug 27 '20 at 11:23
  • @Michael In the case of `ArrayList.forEach`, the interface does nothing. The implementation used by `ArrayList` and `ArrayDeque` don't depend on the `iterator()`, or anything else from `Iterable`. They only override `Iterable.forEach` because they already implemented `Iterable` from the start. They aren't even depending on `Iterable.forEach` (no super call). May as well not be overriding `Iterable.forEach` and simply have each declare it on their own (or introduce a new interface, which I doubt they wanted to do). – Vince Aug 27 '20 at 11:35
  • @Dioxin Both the article and the OP are talking about `Iterable.forEach` in the general case. Neither one made any specific reference to ArrayList. You are moving the goalposts, because you never made any claim in your previous 2 comments that it depends on the implementation. What is true for ArrayList is not true for LinkedList, so you cannot say both are internal iterators because of the implementation of one of them. I even said in my answer "could be overridden". – Michael Aug 27 '20 at 11:41
  • @Michael He's using `List namesList = Arrays.asList(...)` in his example.. And you're right, it's not the same for all collections. But the idea is, the client of the API doesn't control iteration. You can leave it at "it's just declarative", but you can't get upset that other terms exist man. – Vince Aug 27 '20 at 11:42
  • @Dioxin `java.util.ArrayList` != `java.util.Arrays.ArrayList` – Michael Aug 27 '20 at 11:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/220557/discussion-between-dioxin-and-michael). – Vince Aug 27 '20 at 11:50