Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: why can't iterate over an iterator?

I read Why is Java's Iterator not an Iterable? and Why aren't Enumerations Iterable?, but I still don't understand why this:

void foo(Iterator<X> it) {
  for (X x : it) {
    bar(x);
    baz(x);
  }
}

was not made possible. In other words, unless I'm missing something, the above could have been nice and valid syntactic sugar for:

void foo(Iterator<X> it) {
  for (X x; it.hasNext();) {
    x = it.next();
    bar(x);
    baz(x);
  }
}
like image 772
noamtm Avatar asked Apr 08 '10 07:04

noamtm


People also ask

Why is Java iterator not Iterable?

As others have said, an Iterable can be called multiple times, returning a fresh Iterator on each call; an Iterator is used just once. So they are related, but serve different purposes. Frustratingly, however, the "compact for" method works only with an iterable.

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.

Is iterator Iterable Java?

Both Iterator and Iterable are interfaces in Java that look very similar and are often confusing for beginners, but both are two different things. In short, if any class implements the Iterable interface, it gains the ability to iterate over an object of that class using an Iterator.


4 Answers

Most likely the reason for this is because iterators are not reusable; you need to get a fresh Iterator from the Iterable collection each time you want to iterate over the elements. However, as a quick fix:

private static <T> Iterable<T> iterable(final Iterator<T> it){      return new Iterable<T>(){ public Iterator<T> iterator(){ return it; } }; }  //.... {      // ...      // Now we can use:      for ( X x : iterable(it) ){         // do something with x      }      // ... } //.... 

That said, the best thing to do is simply pass around the Iterable<T> interface instead of Iterator<T>

like image 132
Michael Aaron Safyan Avatar answered Nov 14 '22 21:11

Michael Aaron Safyan


but I still don't understand why this [...] was not made possible.

I can see several reasons:

  1. Iterators are not reusable, so a for/each would consume the iterator - not incorrect behavior, perhaps, but unintuitive to those who don't know how the for/each is desugared.
  2. Iterators don't appear "naked" in code all that often so it would be complicating the JLS with little gain (the for/each construct is bad enough as it is, working on both Iterables and arrays).
  3. There's an easy workaround. It may seem a little wasteful to allocate a new object just for this, but allocation is cheap as it is and escape analysis would rid you even of that small cost in most cases. (Why they didn't include this workaround in an Iterables utility class, analogous to Collections and Arrays, is beyond me, though.)
  4. (Probably not true - see the comments.) I seem to recall that the JLS can only reference things in java.lang[citation needed], so they'd have to create an Iterator interface in java.lang which java.util.Iterator extends without adding anything to. Now we have two functionally equivalent iterator interfaces. 50% of the new code using naked iterators will choose the java.lang version, the rest use the one in java.util. Chaos ensues, compatibility problems abound, etc.

I think points 1-3 are very much in line with how the Java language design philosophy seems to go: Don't surprise newcomers, don't complicate the spec if it doesn't have a clear gain that overshadows the costs, and don't do with a language feature what can be done with a library.

The same arguments would explain why java.util.Enumeration isn't Iterable, too.

like image 44
gustafc Avatar answered Nov 14 '22 23:11

gustafc


The for(Type t : iterable) syntax is only valid for classes that implement Iterable<Type>.

An iterator does not implement iterable.

You can iterate over things like Collection<T>, List<T>, or Set<T> because they implement Iterable.

The following code is equivalent:

for (Type t: list) {
    // do something with t
}

and

Iterator<Type> iter = list.iterator();
while (iter.hasNext()) {
    t = iter.next();
    // do something with t
}

The reason this was not made possible, is because the for-each syntax was added to the language to abstract out the Iterator. Making the for-each loop work with iterators would not accomplish what the for-each loop was created for.

like image 33
jjnguy Avatar answered Nov 14 '22 21:11

jjnguy


Actually, you can.

There is very short workaround available on java 8:

for (X item : (Iterable<X>) () -> iterator)

See How to iterate with foreach loop over java 8 stream for the detailed explanation of the trick.

And some explanations why this was not natively supported can be found in related question:

Why does Stream<T> not implement Iterable<T>?

like image 37
Vadzim Avatar answered Nov 14 '22 23:11

Vadzim