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

Has QUIT--Anony-Mousse picture Has QUIT--Anony-Mousse · Jun 27, 2012 · Viewed 78.9k times · Source

Possible Duplicate:
Why is Java's Iterator not an Iterable?

Idiomatic way to use for-each loop given an iterator?

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?

Answer

T.J. Crowder picture T.J. Crowder · Jun 27, 2012

Does anyone know why the language was designed this way?

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

Compare: I start out with an iterable:

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

// New way
for (Thingy t : iterable) {
    // Use `t`
}

Versus I start out with an iterator:

// Old/current way
while (iterator.hasNext()) {
    Thing t = iterator.next();
    // Use `t`
}

// Imagined way
for (Thingy t : iterator) {
   // Use `t`
}

There's not much in it in the second example, and it complicates the semantics of the for-each by creating a special case.

"Why" questions are always hard when not directed at the principal participants involved in the decision, but my guess is that the added complexity wasn't worth the marginal utility.


That said, I could see an "enhanced while loop" construct:

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

...which picks up where the iterator currently is... Meh, maybe it would confuse people too much. :-)