Is an "infinite" iterator bad design?

JavaCollectionsIterator

Java Problem Overview


Is it generally considered bad practice to provide Iterator implementations that are "infinite"; i.e. where calls to hasNext() always(*) return true?

Typically I'd say "yes" because the calling code could behave erratically, but in the below implementation hasNext() will return true unless the caller removes all elements from the List that the iterator was initialised with; i.e. there is a termination condition. Do you think this is a legitimate use of Iterator? It doesn't seem to violate the contract although I suppose one could argue it's unintuitive.

public class CyclicIterator<T> implements Iterator<T> {
  private final List<T> l;
  private Iterator<T> it;

  public CyclicIterator<T>(List<T> l) {
    this.l = l;
    this.it = l.iterator();
  }

  public boolean hasNext() {
    return !l.isEmpty();
  }

  public T next() {
    T ret;

    if (!hasNext()) {
      throw new NoSuchElementException();
    } else if (it.hasNext()) {
      ret = it.next();
    } else {
      it = l.iterator();
      ret = it.next();
    }

    return ret;
  }

  public void remove() {
    it.remove();
  }
}

(Pedantic) EDIT

Some people have commented how an Iterator could be used to generate values from an unbounded sequence such as the Fibonacci sequence. However, the Java Iterator documentation states that an Iterator is:

> An iterator over a collection.

Now you could argue that the Fibonacci sequence is an infinite collection but in Java I would equate collection with the java.util.Collection interface, which offers methods such as size() implying that a collection must be bounded. Therefore, is it legitimate to use Iterator as a generator of values from an unbounded sequence?

Java Solutions


Solution 1 - Java

I think it is entirely legitimate - an Iterator is just a stream of "stuff". Why should the stream necessarily be bounded?

Plenty of other languages (e.g. Scala) have the concept of unbounded streams built in to them and these can be iterated over. For example, using scalaz

scala> val fibs = (0, 1).iterate[Stream](t2 => t2._2 -> (t2._1 + t2._2)).map(_._1).iterator
fibs: Iterator[Int] = non-empty iterator

scala> fibs.take(10).mkString(", ") //first 10 fibonnacci numbers
res0: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

EDIT: In terms of the principle of least surprise, I think it depends entirely on the context. For example, what would I expect this method to return?

public Iterator<Integer> fibonacciSequence();

Solution 2 - Java

The whole point of an Iterator is that it is lazy, i.e. that it gives you only as many objects as you ask for. If a user asks for all objects of an infinite Iterator, it's their problem, not yours.

Solution 3 - Java

While I too think that it's legitimate, I'd like to add that such an Iterator (or more precisely: an Iterable producing such an Iterator) would not play well with the enhanced for-loop (a.k.a for-each-loop):

for (Object o : myIterable) {
   ...
}

Since the code inside an enhanced for loop has no direct access to the iterator, it couldn't call remove() on the iterator.

So to end the looping it would have to do one of the following:

  • Get access to the internal List of the Iterator and remove objects directly, possibly provoking a ConcurrentModificationException
  • use break to exit the loop
  • "use" an Exception to exit the loop
  • use return to leave the loop

All but the last of those alternatives aren't exactly the best way to leave an enhanced for-loop.

The "normal" for-loop (or any other loop together with an explicit Iterator variable) would work just fine, of course:

for (Iterator it = getIterator(); it.hasNext();) {
  Object o = it.next()
  ...
}

Solution 4 - Java

This may be semantics, but the iterator should diligently return the next item without regard to when it will end. Ending is a side effect for a collection, though it may seem like a common side effect.

Still, what's the difference between infinite, and a collection with 10 trillion items? The caller either wants them all, or he doesn't. Let the caller decide how many items to return or when to end.

I wouldn't say the caller couldn't use the for-each construct. He could, as long as he wants all the items.

Something in the documentation for the collection like "may be an infinite collection" would be appropriate, but not "infinite iterator".

Solution 5 - Java

It is a perfectly legitimate use - as long as it is properly documented.

Using the name CyclicIterator is a good idea, as it infers that looping on the iterator might well possibly be infinite if the loop exit case is not properly defined.

Solution 6 - Java

An infinite iterator is very useful when you create infinite data, e.g. linear recurrent sequences like the Fibonacci sequence.

So it is totally fine to use such.

Solution 7 - Java

Yes. You just need a different criteria for when to stop iterating.

The concept of infinite lists is very common in lazy evaluation languages like Haskell and leads to completely different program designs.

Solution 8 - Java

Actually, an Iterator comes from an Iterable, not a Collection, whatever the JavaDoc API says. The latter extends the former, and that's why it can generate an Iterator as well, but it's perfectly reasonably for an Iterable not to be a Collection. In fact, there are even a few Java classes which implement Iterable but not Collection.

Now, as for expectations... of course an infinite iterator must not be made available without a clear indication that this happens to be the case. But, sometimes, expectations can be deceiving. For instance, one might expect an InputStream to always come to an end, but that really isn't a requirement of it. Likewise, an Iterator returning lines read from such a stream might never end.

Solution 9 - Java

Consider whether you have a generator function. An iterator may be a reasonable interface to a generator, or it might not, as you've noticed.

On the other hand, Python has generators which do terminate.

Solution 10 - Java

I can imagine any number of uses for an infinite iterator. Like, what about a program iterating through status messages being sent by another server or a background process. While in real life the server will presumably stop eventually, from the program's point of view it might just keep reading until it is stopped by something unrelated to the iterator reaching its end.

It would certainly be unusual and, as others have said, should be carefully documented. But I wouldn't declare it unacceptable.

Solution 11 - Java

If you have an object that needs an iterator, and you happen to give it an infinite iterator, then all should be well. Of course, that object should never require the iterator to stop. And that is the potentially bad design.

Think of a music player. You tell it to start playing tracks in "continuous play mode" and it plays tracks until you tell it to stop. Maybe you implement this continuous play mode by shuffling a playlist forever... until the user presses "stop". In this case, MusicPlayer needs to iterator over a playlist, that might be Collection<Track>, forever. In this case, you'd want either a CyclicIterator<Track> as you've built or a ShuffleIterator<Track> that randomly picks the next track and never runs out. So far, so good.

MusicPlayer.play(Iterator<Track> playlist) wouldn't care whether the play list ends or not. It works both with an iterator over a list of tracks (play to the end, then stop) and with a ShuffleIterator<Track> over a list of tracks (pick a random track forever). All still good.

What if someone tried to write MusicPlayer.playContinuously()--which expects to play tracks "forever" (until someone presses "stop"), but accepts an Iterator<Track> as the parameter? That would be the design problem. Clearly, playContinuously() needs an infinite Iterator, and if it accepts a plain Iterator as its parameter, then that would be bad design. It would, for example, need to check hasNext() and handle the case where that returns false, even though it should never happen. Bad.

So implement all the infinite iterators you want, as long as their clients don't depend on those iterators ending. As if your client depends on the iterator going on forever, then don't give it a plain Iterator.

This should be obvious, but it doesn't seem to be.

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
QuestionAdamskiView Question on Stackoverflow
Solution 1 - Javaoxbow_lakesView Answer on Stackoverflow
Solution 2 - JavaJörg W MittagView Answer on Stackoverflow
Solution 3 - JavaJoachim SauerView Answer on Stackoverflow
Solution 4 - JavaMarcus AdamsView Answer on Stackoverflow
Solution 5 - JavaYuval AdamView Answer on Stackoverflow
Solution 6 - JavaFelix KlingView Answer on Stackoverflow
Solution 7 - JavaThorbjørn Ravn AndersenView Answer on Stackoverflow
Solution 8 - JavaDaniel C. SobralView Answer on Stackoverflow
Solution 9 - JavaPotatoswatterView Answer on Stackoverflow
Solution 10 - JavaJayView Answer on Stackoverflow
Solution 11 - JavaJ. B. RainsbergerView Answer on Stackoverflow