Skip to main content

Why is Java"s Iterator not an Iterable?



Why does the Iterator interface not extend 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.listSomObjects()) {

....

}







where listSomeObject returns an iterator.


Comments

  1. 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.

    ReplyDelete
  2. An iterator is stateful. The idea is that if you call Iterable.iterator() twice you'll get independent iterators. 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.

    ReplyDelete
  3. 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,

    ReplyDelete
  4. An Iterable is a thing from which you obtain an Iterator.

    ReplyDelete
  5. 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.

    ReplyDelete
  6. 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();
    }

    ReplyDelete
  7. 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>

    ReplyDelete
  8. 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(IterableIterator iter)
    {
    this.initdata = iter.initdata;
    }
    // methods of Iterable

    public Iterator iterator() {
    return new MyClass(this.somedata);
    }

    // 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 classes next() and hasNext()

    ReplyDelete
  9. As an aside: Scala has a toIterable() method in Iterator. See scala implicit or explicit conversion from iterator to iterable

    ReplyDelete
  10. Visage allready said that these are both different concepts introduced in different versions of Java. Iterator was introduced first, that's only one of the reasons it doesn't extend Iterable.

    But nothing can keep you from implementing both interfaces in one class. In fact this is the solution I see programmers use very often.

    public class MyClass implements Iterator, Iterable {

    // methods of Iterable

    public Iterator iterator() {
    return this;
    }

    // methods of Iterator

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

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

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

    }

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex