86

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?

Community
  • 1
  • 1
Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
  • 1
    Or [this one](http://stackoverflow.com/questions/2162917/can-we-use-for-each-loop-for-iterating-the-objects-of-iterator-type). – Dave Newton Jun 26 '12 at 22:53
  • 1
    Both close duplicates, but not entirely the same. Iterator shouldn't be Iterable, because you can only iterate once (no "restarts"). And I know that you can't use foreach on iterators (unless you use the adapter I outlined), but I'm trying to understand why Java was designed this way. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:11
  • 1
    Meh; I tend to disagree. The answer given in the 1.5 docs states simply that the designers chose this way to handle the most common use-cases. Sometimes the answer *is* "just because". – Dave Newton Jun 26 '12 at 23:13
  • 1
    Sometimes the designers *have* a reason to design the language one way or another... what is wrong with trying to understand their reasoning (even if I won't be able to change things)? – Has QUIT--Anony-Mousse Jun 26 '12 at 23:22
  • 1
    Who said there was anything wrong with trying to understand their reasoning? What I *said* was that they decided to grab low-hanging fruit, this was the way they did it, and there isn't necessarily very much to understand. – Dave Newton Jun 26 '12 at 23:34
  • Well, from the discussions below, it does not look to me as if it was just a "low-hanging fruit" decision; but there are actually some arguments to do it the way they did it. A pity that you are so dismissive against language design discussions. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:40
  • 1
    [This answer](http://stackoverflow.com/a/3883979/771837) lists some potential reasons. – trutheality Jun 26 '12 at 23:58
  • 1
    And it's a pity you continue to misunderstand me, but whatever. IMO everything below *supports* it being a conscious decision to support the most-common use-cases, which is precisely what "low-hanging fruit" is. Further bolstered by the verbiage I quoted from the 1.5 for-each docs where they say precisely the same thing. Good luck! – Dave Newton Jun 27 '12 at 00:05
  • @trutheality: thank you. Yes, this was the kind of answer I was looking for. – Has QUIT--Anony-Mousse Jun 27 '12 at 00:13
  • The real question, which people rarely seem to be willing to ask, is why are we using Java? It's an endless pile of terrible choices, and it seems like for every good choice they make in each new version, they make dozens of terrible choices. – ArtOfWarfare Mar 03 '17 at 20:12
  • @BasilBourque the question was on `Iterator` not `Iterable`. – Has QUIT--Anony-Mousse Oct 03 '19 at 01:52

4 Answers4

24

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.

Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
  • 1
    *"With the Iterator syntax, one would need to write..."* Why? – T.J. Crowder Jun 26 '12 at 23:29
  • Because otherwise I could have a class implement both `Iterable` and `Iterator` and cause big time confusion. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:31
  • And what does that have to do with arrays? I can do that anyway, without reference to arrays -- and it's *exactly* the sort of complexity I call out in my answer (slash "guess" :-) ). – T.J. Crowder Jun 26 '12 at 23:33
  • You want to have the same syntax for arrays that you have for collections, and collections are `Iterable`, but not `Iterator`s. So while one could setup a for-each loop on Iterators, too, one will definitely need it for arrays and array-like structures such as collections consistently. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:38
  • @ Anony-Mousse: Huh? Is it your argument now that you want for-each to **only** work on iterators and not iterables? You've lost me somewhere. – T.J. Crowder Jun 26 '12 at 23:41
  • I'm looking at all the alternatives (to understand why they picked the one they picked). One rather obvious would be to allow Iterators only, yes. As you can just call `iterator()` to get one from an iterable, that would be a minimal restriction. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:49
  • To me, the objection to option C) make no sense. How rare programmers implements both `Iterator` and `Iterable`? If I have seen one I would consider that's very bad code since it mixes up different responsibilities into a same class. So the compiler shall flag this as an error or at lease a warning and use `Iterable` beats `Iterator` strategy. Also, if you have two foreaches apply to the same `Iterator`, A programmer shall know that the status of the `Iterator` was altered in the first loop and it is likely that the second loop will not do anything since `hasNext` may return `false`. – Earth Engine Nov 19 '14 at 01:20
14

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Thanks for that pointer. I now have a rough idea of what might be the reason. I'll have to think a bit more on that lines, then I'll update. – Has QUIT--Anony-Mousse Jun 26 '12 at 23:13
  • 6
    Apparently, the rationale for introducing `java.lang.Iterable` instead of just using `java.util.Iterator` is to avoid a core language dependency on `java.util`. ;) – 200_success Apr 30 '14 at 19:04
  • 1
    A strange rationale, considering that the loop still depends on the type `java.util.Iterator`, just returned from the `Iterable`’s `iterator()` method. – Holger Sep 21 '22 at 08:13
9

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.

Matt
  • 11,523
  • 2
  • 23
  • 33
  • Well, if the "reset" is done explicitely by calling `.iterator()` on `Iterable`s, the reset should also be obvious. Well, one can probably still overlook not resetting... – Has QUIT--Anony-Mousse Jun 26 '12 at 23:46
  • Iterable gives you a predictable looping mechanism in that looping over an iterable multiple times yields the same result every time (assuming you haven't mucked with the data itself). The same cannot be said if you could loop over an Iterator (since as I said, it can't be reset). – Matt Jun 27 '12 at 00:01
  • For some data types like queues, the notion of a consuming "for each" makes sense. Allowing types to be "for-each-able" without a promise of being *repeatedly* enumerable, would seem like a helpful ability, and iterators are inherently capable of being read from exactly once. – supercat Apr 29 '14 at 20:25
  • That opinion is inconsistent with try-with-resources and `Stream.forEach()` – Lukas Eder Dec 20 '16 at 19:30
1

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.

monojohnny
  • 5,894
  • 16
  • 59
  • 83
Jochen
  • 2,277
  • 15
  • 22
  • technically, i think the bytecode looks more like a for loop: for (Iterator iterator = iterable.iterator(); iterator.hasNext();) – Matt Jun 27 '12 at 00:03
  • In fact in the JSR where they proposed the for each syntax, they used this kind of *for* loop. The reason is variable scoping, the Iterator then is only defined inside the loop. The while syntax is classic Java textbook, but you'd need to add an extra `{}` around to get the variable scoping right, too. – Has QUIT--Anony-Mousse Jun 27 '12 at 00:07
  • 1
    I wonder which text book, as I’ve always seen the `for` loop. – Holger Sep 21 '22 at 08:18