Why does Java not allow foreach on iterators (only on iterables)?

JavaSyntaxIteratorLanguage DesignIterable

Java Problem Overview


> Possible Duplicate:
> Why is Java's Iterator not an Iterable?
> > https://stackoverflow.com/questions/3883131/idiomatic-way-to-use-for-each-loop-given-an-iterator > > https://stackoverflow.com/questions/2162917/can-we-use-for-each-loop-for-iterating-the-objects-of-iterator-type

The foreach loop are as far as I know syntax sugar added in Java 5. 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 an iterable in the first place, but only an iterator (say, because a class offers two different iterators), I cannot use the syntax sugar foreach loop. Obviously I can still do the plain old style iteration. However, I'd actually like to do:

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

And of course I can do a 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 in fact is an ugly abuse of the Iterable API, as it can only be iterated once!)

If it were designed around Iterator instead of iterable, one could do a number of interesting things:

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 a class would implement both Iterator and Iterable? To avoid programmer errors that assume that "for(O o : iter)" will process all elements twice (and forget to get a fresh iterator)? Or is there some other reason for this?

Or is there some language trick I just do not know?

Java Solutions


Solution 1 - Java

So I have a somewhat reasonable explanation now:

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

If the syntax were designed around Iterator as I proposed, it would be inconsistent with arrays. Let me give three variants:

A) as chosen by the 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(); }

The behaves the same way and is highly consistent across arrays and collections. Iterators however have to use the classic iteration style (which at least is not likely to 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) { }

Now arrays and collections are inconsistent; but arrays and ArrayList are very closely related and should behave the same way. Now if at any point, the language is extended to make e.g. 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 end up in unclear situations when either someone implements both Iterable and Iterator (is the for loop supposed to get a new iterator or iterate over the current - happens easily in tree-like structures!?!). A simple tie-braker ala "Iterable beats Iterator" unfortunately won't do: it suddenly introduces runtime vs. compile time difference and generics issues.

Now suddenly, we need to pay attention to whether we want to iterate over collections/iterables or arrays, at which point we have gained very little benefits at the cost of a big confusion.

The way "for each" is in Java (A) is very consistent, it causes very little programming errors, and it allows for the possible future change of turning arrays into regular objects.

There is a variant D) that would probably also work okay: for-each for Iterators only. Preferrably by adding a .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 environment, not just the compiler, and breaks backwards compatibility. Plus, the mentioned confusion is still present that

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

Only iterates over the data once.

Solution 2 - Java

The Iterable interface was created exactly for that purpose (enhanced for loop) as described in the original JSR, although the Iterator interface was already in use.

Regarding the new interfaces discussed in the JSR (note the package names):

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator (proposed in JSR to be retrofitted onto java.util.Iterator though not actually done)

…the JSR says:

>These new interfaces serve to prevent the dependency of the language on java.util that would otherwise result.

Solution 3 - Java

Because the "for" loop would be destructive to the iterator. Iterator cannot be reset (ie. moved back to the beginning) unless it implements the ListIterator subinterface.

Once you put an Iterator through a "for" loop it would no longer useable. My guess is the language designers decided that this combined with the additional special cases (of which there are already two for Iterable and arrays) in the compiler to translate this into bytecode (you couldn't reuse the same transformation as iterable) was enough of a detractor to not implement it.

When you do this yourself in the code via the iterator interface, it would at least be apparantly obvious what's going on.

With lambdas coming they could make this nice 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 a relatively simple syntax. I doubt they would built methods like "each", "collect" and "map" into the different interfaces because that would break backward compatibilty, plus you'd have arrays still to deal with.

Solution 4 - Java

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

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

occurred all the time in old-style code. And doing fancy things with multiple iterators did not.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHas QUIT--Anony-MousseView Question on Stackoverflow
Solution 1 - JavaHas QUIT--Anony-MousseView Answer on Stackoverflow
Solution 2 - JavaassyliasView Answer on Stackoverflow
Solution 3 - JavaMattView Answer on Stackoverflow
Solution 4 - JavaJochenView Answer on Stackoverflow