Why doesn't Java allow foreach on iterators (only on iterations)?

Possible duplicate:
Why is Java Iterator not iterable?

An idiomatic use case for each loop given by an iterator?

Is it possible to use a for loop for each iteration of objects of type Iterator?

The foreach loop is as far as I know that syntax sugar has been added to Java. so

Iterable<O> iterable; for(O o : iterable) { // Do something } 

will essentially produce the same bytecode as

 Iterable<O> iterable; for(Iterator<O> iter = iterable.iterator(); iter.hasNext(); /* NOOP */) { O o = iter.next(); // Do something } 

However, if I do not have iteration in the first place, but only an iterator (say, because the class offers two different iterators), I cannot use the foreach syntax loop. Obviously, I can still do the usual old style iteration. However, I would really like to:

 Iterator<O> iter; for(O o : iter /* Iterator<O>, not Iterable<O>! */) { // Do something } 

And of course, I can make it fake Iterable :

 class Adapter<O> implements Iterable<O> { Iterator<O> iter; public Adapter(Iterator<O> iter) { this.iter = iter; } @Override public Iterator<O> iterator() { return iter; } } 

(which is actually an ugly abuse of the Iterable API, as it can only be repeated once!)

If it was created around an Iterator instead of an iterable, a few interesting things could be done:

 for(O o : iterable.iterator()) {} // Iterate over Iterable and Collections for(O o : list.backwardsIterator()) {} // Or backwards Iterator<O> iter; for(O o : iter) { if (o.something()) { iter.remove(); } if (o.something()) { break; } } for(O : iter) { } // Do something with the remaining elements only. 

Does anyone know why the language was designed this way? To avoid ambiguity, if the class will implement both Iterator and Iterable ? To avoid programmer errors suggesting that "for (O o: iter)" will process all elements twice (and forget to get a fresh iterator)? Or is there another reason for this?

Or is there some kind of trick that I just don't know?

+68
java iterator syntax language-design iterable
Jun 26 '12 at 22:45
source share
5 answers

So now I have a somewhat reasonable explanation:

Short version: Because the syntax also applies to arrays that don't have iterators.

If the syntax was designed around Iterator , as I suggested, it will not be compatible with arrays. Let me give you three options:

A) selected by Java developers:

 Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list) { } Iterator<Object> iter; while(iter.hasNext()) { Object o = iter.next(); } 

It behaves the same and is very consistent between arrays and collections. Iterators, however, should use the classic iteration style (which, at least, does not cause errors).

B) allow arrays and Iterators :

 Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list.iterator()) { } Iterator<Object> iter; for(Object o : iter) { } 

Arrays and collections are now incompatible; but arrays and ArrayList are very closely related and should behave the same. Now, if at any moment the language expands to make, for example, arrays implement Iterable , it becomes inconsistent.

C) allow all three:

 Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list) { } Iterator<Object> iter; for(Object o : iter) { } 

Now, if we find ourselves in unclear situations, when someone realizes both Iterable and Iterator (this is a for loop that should get a new iterator or iterate over the current one - it happens easily in tree structures!?!). Unfortunately, a simple tie-braker ala "Iterable beats Iterator" will not do: it unexpectedly introduces runtime and compile time difference and generic problems.

Now, unexpectedly, we need to pay attention to whether we want iterations over collections / iterators or arrays, and at that moment we got very few advantages due to a lot of confusion.

The "for everyone" method is very consistent in Java (A), it causes very few programming errors, and this allows a possible future change in the rotation of arrays into ordinary objects.

There is option D) , which will probably also work fine: for each for iterators. Preferably by adding the .iterator() method to primitive arrays:

 Object[] array; for(Object o : array.iterator()) { } Iterable<Object> list; for(Object o : list.iterator()) { } Iterator<Object> iter; for(Object o : iter) { } 

But this requires changes to the runtime, not just the compiler, and interruptions to backward compatibility. In addition, the above confusion is still present that

 Iterator<Object> iter; for(Object o : iter) { } for(Object o : iter) { } 

Only one repetition of data is performed.

+20
Jun 26 '12 at 23:23
source share

Does anyone know why the language was designed this way?

Because for-each only makes sense for things that are iterative, and doesn't make sense for iterators. If you already have an iterator, you already have what you need to do this with a simple loop.

Compare: I start by repeating:

 // Old way Iterator<Thingy> it = iterable.iterator(); while (it.hasNext()) { Thingy t = it.next(); // Use 't' } // New way for (Thingy t : iterable) { // Use 't' } 

Compared to what I start with an iterator:

 // Old/current way while (iterator.hasNext()) { Thing t = iterator.next(); // Use 't' } // Imagined way for (Thingy t : iterator) { // Use 't' } 

In the second example, this is not so much, and this complicates the semantics for each, creating a special case.

The why questions are always difficult when they are not aimed at the main participants involved in the solution, but I assume that the additional complexity was not worth the utmost usefulness.




However, I could see the "extended in while loop" construct:

 while (Thingy t : iterator) { // Use 't' } 

... which determines where the iterator is now ... Fur, maybe this will confuse people too much. :-)

+37
Jun 26 '12 at 22:50
source share

The Iterable interface was created just for this purpose (enhanced for the loop), as described in the original JSR , although the Iterator interface has already been enabled to use.

+9
Jun 26 2018-12-12T00:
source share

Because the for loop will be destructive for the iterator. An iterator cannot be reset (i.e., returned to the beginning) if it does not implement the ListIterator subordinate interface.

Once you install the Iterator through the for loop, it will no longer be used. I assume that the language developers decided that this combined with additional special cases (of which there are already two for Iterable and arrays) in the compiler to translate this into bytecode (you could not reuse the same conversion as iterative ), there was enough detractor not to realize it.

When you do this yourself in code through an iterator interface, it will be at least sure what is going on.

With the advent of lambdas, they can make it beautiful and easy:

 Iterator<String> iterator = ...; Collections.each ( iterator, (String s) => { System.out.println(s); } ); List<String> list = ...; Collections.each ( list, (String s) => { System.out.println(s); } ); 

without breaking backward compatibility and still having relatively simple syntax. I doubt that they built methods such as "each", "assemble" and "map" to different interfaces, because that would break backward compatibility, plus you would still have arrays.

+7
Jun 26 '12 at 23:26
source share

I think one part of the answer may be hidden in the fact that syntactic sugar is used for each cycle. The fact is that you want to do something that people do a lot, much easier. And (at least in my experience) an idiom

 Iterator iterator = iterable.iterator(); while( iterator.hasNext() ) { Element e = (Element)iterator.next() ; } 

happened all the time in old-style code. And doing fancy things with several iterators didn’t.

+1
Jun 26 2018-12-12T00:
source share



All Articles