Why is Java's Iterator not an Iterable?

JavaIteratorIterable

Java Problem Overview


Why does the Iterator interface not extend Iterable?

The iterator() method could simply return this.

Is it on purpose or just an oversight of Java's designers?

It would be convenient to be able to use a for-each loop with iterators like this:

for(Object o : someContainer.listSomeObjects()) {
    ....
}

where listSomeObjects() returns an iterator.

Java Solutions


Solution 1 - Java

An iterator is stateful. The idea is that if you call Iterable.iterator() twice you'll get independent iterators - for most iterables, anyway. That clearly wouldn't be the case in your scenario.

For example, I can usually write:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

That should print the collection twice - but with your scheme the second loop would always terminate instantly.

Solution 2 - Java

Because an iterator generally points to a single instance in a collection. Iterable implies that one may obtain an iterator from an object to traverse over its elements - and there's no need to iterate over a single instance, which is what an iterator represents.

Solution 3 - Java

For my $0.02, I completely agree that Iterator should not implement Iterable, but I think the enhanced for loop should accept either. I think the whole "make iterators iterable" argument comes up as a work around to a defect in the language.

The whole reason for the introduction of the enhanced for loop was that it "eliminates the drudgery and error-proneness of iterators and index variables when iterating over collections and arrays" [[1]].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

Why then does this same argument not hold for iterators?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

In both cases, the calls to hasNext() and next() have been removed, and there is no reference to the iterator in the inner loop. Yes, I understand that Iterables can be re-used to create multiple iterators, but that all happens outside of the for loop: inside the loop there is only ever a forward progression one item at a time over the items returned by the iterator.

Also, allowing this would also make it easy to use the for loop for Enumerations, which, as has been pointed out elsewhere, are analogous to Iterators not Iterables.

So don't make Iterator implement Iterable, but update the for loop to accept either.

Cheers,

[1]: http://download.oracle.com/javase/1.5.0/docs/relnotes/features.html#forloop "[1]"

Solution 4 - Java

As pointed out by others, Iterator and Iterable are two different things.

Also, Iterator implementations predate enhanced for loops.

It is also trivial to overcome this limitation with a simple adapter method that looks like this when used with static method imports:

for (String line : in(lines)) {
  System.out.println(line);
}

Sample implementation:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

In Java 8 adapting an Iterator to an Iterable gets simpler:

for (String s : (Iterable<String>) () -> iterator) {

Solution 5 - Java

Incredibly, no one else has given this answer yet. Here's how you can "easily" iterate over an Iterator by using the new Java 8 Iterator.forEachRemaining() method:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

Of course, there's a "simpler" solution that works with the foreach loop directly, wrapping the Iterator in an Iterable lambda:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

Solution 6 - Java

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.

What I will describe below is one way to have the best of both worlds - returning an Iterable (for nicer syntax) even when the underlying sequence of data is one-off.

The trick is to return an anonymous implementation of the Iterable that actually triggers the work. So instead of doing the work that generates a one-off sequence and then returning an Iterator over that, you return an Iterable which, each time it is accessed, redoes the work. That might seem wasteful, but often you will only call the Iterable once anyway, and even if you do call it multiple times, it still has reasonable semantics (unlike a simple wrapper that makes an Iterator "look like" an Iterable, this won't fail if used twice).

For example, say I have a DAO that provides a series of objects from a database, and I want to provide access to that via an iterator (eg. to avoid creating all objects in memory if they are not needed). Now I could just return an iterator, but that makes using the returned value in a loop ugly. So instead I wrap everything in an anon Iterable:

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

this can then be used in code like this:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

which lets me use the compact for loop while still keeping incremental memory use.

This approach is "lazy" - the work is not done when the Iterable is requested, but only later when the contents are iterated over - and you need to be aware of the consequences of that. In the example with a DAO that means iterating over the results within the database transaction.

So there are various caveats, but this can still be a useful idiom in many cases.

Solution 7 - Java

Iterator is an interface which allows you to iterate over something. It is an implementation of moving through a collection of some kind.

Iterable is a functional interface which denotes that something contains an accessible iterator.

In Java8, this makes life pretty easy... If you have an Iterator but need an Iterable you can simply do:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

This also works in the for-loop:

for (T item : ()->someIterator){
    //doSomething with item
}

Solution 8 - Java

I also see many doing this:

public Iterator iterator() {
    return this;
}

But that does not make it right! This method would not be what you want!

The method iterator() is supposed to return a new iterator starting from scratch. So one need to do something like this:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(SomeType initdata)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new IterableIterator(this.intidata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

The question is: would there be any way to make an abstract class performing this? So that to get an IterableIterator one only need to implement the two methods next() and hasNext()

Solution 9 - Java

I agree with the accepted answer, but want to add my own explanation.

  • Iterator represents the state of traversing, e.g., you can get the current element from an iterator and move forward to the next.

  • Iterable represents a collection which could be traversed, it can return as many iterators as you want, each representing its own state of traversing, one iterator may be pointing to the first element, while another may be pointing to the 3rd element.

It would be nice if Java for loop accepts both Iterator and Iterable.

Solution 10 - Java

To avoid dependence on java.util package

According to the original JSR, An enhanced for loop for the Java™ Programming Language, the proposed interfaces:

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator
    (proposed to be retrofitted onto java.util.Iterator, but apparently this never happened)

…were designed to use the java.lang package namespace rather than java.util.

To quote the JSR:

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


By the way, the old java.util.Iterable gained a new forEach method in Java 8+, for use with lambda syntax (passing a Consumer).

Here is an example. The List interface extends Iterable interface, as any list carries a forEach method.

List
.of ( "dog" , "cat" , "bird" )
.forEach ( ( String animal ) -> System.out.println ( "animal = " + animal ) );

Solution 11 - Java

If you came here in search of a workaround, you can use IteratorIterable. (available for Java 1.6 and above)

Example usage (reversing a Vector).

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
    	Vector<String> vs = new Vector<String>();
	    vs.add("one");
	    vs.add("two");
	    for ( String s: vs ) {
	        System.out.println(s);
	    }
	    Iterable<String> is
	        = new IteratorIterable(new ReverseListIterator(vs));
	    for ( String s: is ) {
	        System.out.println(s);
	    }
    }
}

prints

one
two
two
one

Solution 12 - Java

For the sake of simplicity, Iterator and Iterable are two distinct concepts, Iterable is simply a shorthand for "I can return an Iterator". I think that your code should be:

for(Object o : someContainer) {
}

with someContainer instanceof SomeContainer extends Iterable<Object>

Solution 13 - Java

Solution 14 - Java

On a related note, you may find the IteratorIterable adapter in Apache Commons Collections4 useful. Just create an instance from an iterator, and you have the corresponding iterable.

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID: org.apache.commons:commons-collections4:4.0

Solution 15 - Java

Iterators are stateful, they have a "next" element and become "exhausted" once iterated over. To see where the problem is, run the following code, how many numbers are printed?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

Solution 16 - Java

You can try the following example :

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}

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
QuestionŁukasz BownikView Question on Stackoverflow
Solution 1 - JavaJon SkeetView Answer on Stackoverflow
Solution 2 - JavaPaulJWilliamsView Answer on Stackoverflow
Solution 3 - JavaBarneyView Answer on Stackoverflow
Solution 4 - JavaMcDowellView Answer on Stackoverflow
Solution 5 - JavaLukas EderView Answer on Stackoverflow
Solution 6 - Javaandrew cookeView Answer on Stackoverflow
Solution 7 - JavaSteve KView Answer on Stackoverflow
Solution 8 - JavaMartin VatshelleView Answer on Stackoverflow
Solution 9 - JavaDagangView Answer on Stackoverflow
Solution 10 - JavaBasil BourqueView Answer on Stackoverflow
Solution 11 - Javaserv-incView Answer on Stackoverflow
Solution 12 - JavadfaView Answer on Stackoverflow
Solution 13 - JavaCatweazleView Answer on Stackoverflow
Solution 14 - JavaFabrizioView Answer on Stackoverflow
Solution 15 - JavaRolandView Answer on Stackoverflow
Solution 16 - JavaSakthi KingView Answer on Stackoverflow