Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 963
Has QUIT--Anony-Mousse Avatar asked Jun 26 '12 22:06

Has QUIT--Anony-Mousse


People also ask

Why are iterators not copyable?

Realize that elements could be mutable (or reference dynamic values) and they may reference other structures and semantics which require a customized copy function. Thus copyable iterators is not orthogonal to copyable stream elements, so this is why copyable iterators are not possible in general.

Can you use for-each on an iterator?

Collections can be iterated easily using two approaches. Using for-Each loop − Use a foreach loop and access the array using object. Using Iterator − Use a foreach loop and access the array using object.

Are all iterators Iterables?

Note that every iterator is also an iterable, but not every iterable is an iterator. For example, a list is iterable but a list is not an iterator. An iterator can be created from an iterable by using the function iter().

What can foreach loops not be used for?

For-each cannot be used to initialize any array or Collection, because it loops over the current contents of the array or Collection, giving you each value one at a time. The variable in a for-each is not a proxy for an array or Collection reference.


1 Answers

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.

like image 84
Has QUIT--Anony-Mousse Avatar answered Sep 22 '22 00:09

Has QUIT--Anony-Mousse