views:

943

answers:

10

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?

+44  A: 

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();
oxbow_lakes
+1 A very good point. It changed my mind.
Justin Niessner
The concept as such is definitely legitimate. An important question is still if such an `Iterator` would violate the principle of least suprise **specifically** in Java.
Joachim Sauer
@Joachim - I think it entirely depends on the context. See edit
oxbow_lakes
@oxbow: of course and in that case I'd have no problem whatsoever. The thing that could get messy is if an `Iterable` would return such an `Iterator` (see my answer). There is an unspoken assumption that an `Iterable` basically behaves like a limited `Collection` i.e. they return a finite number of objects.
Joachim Sauer
The principle is not a dogma. Feel free to mke an exception BUT MAKE IT OBVIOUS, that it's an exception. Like, document this behavior in bold on top of the function description and make the name self-explainatory.
SF.
+1. I've done the same thing with the yield keyword in C#.
TrueWill
+2  A: 

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.

Potatoswatter
+2  A: 

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.

Yuval A
Given this would you say that an API should return a CyclicIterator rather than an Iterator in situations where this is used? It makes it very explicit but makes it impossible to change the implementation transparently, and doesn't actually provide any additional methods in its implementation.
Adamski
Given the appropriate use-case I would consider that - absolutely.
Yuval A
Doesn't *CyclicIterator* implies the iterator runs... well in cycles? What's wrong with *InfiniteIterator*?
Willi
Ok, that was a dumb comment... *head-slapping-myself*. Next time i'll read the question before writing bullshit.
Willi
+4  A: 

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.

Felix Kling
I've been thinking about this and am not sure whether it goes against the definition that an Iterator is "An iterator over a collection". See my latest edit.
Adamski
+7  A: 

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

Jörg W Mittag
+4  A: 

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()
  ...
}
Joachim Sauer
... or use "return" to get out of the loop, which might actually be a nice way to get out if the intent of the loop is to find a specific element.
Rasmus Kaj
I don’t agree. At least `break` (and `return`) are completely acceptable ways to leave a loop.
Konrad Rudolph
@Konrad: they are acceptable, but the enhanced for-loop **strongly** implies that I want to iterate over *every* element the `Iterator` provides. Similarly almost no-one would expect a `break` or `return` in a loop that starts like this: `for (int i=0; i<10; i++)`.
Joachim Sauer
Use break or return. If you have an Iterable<T>, you must assume that it's an unending stream of Ts unless you have some other guarantee that it's not.
James Moore
+4  A: 

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

Marcus Adams
Yes! Think about a webserver: it's just a for each loop over an infinite stream of HTTP requests. Why the hell should it terminate? In fact, if a webserver *does* terminate, we usually call that a "crash" and get all upset about it. You *want* the webserver to be an infinite loop!
Jörg W Mittag
I'm with you. If something returns an Iterable<T>, callers must assume that it won't terminate. They should only assume that they can operate on each item as it comes to them.
James Moore
+3  A: 

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.

Thorbjørn Ravn Andersen
+1  A: 

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.

Jay
Additional thought: While I have already expressed agreement with those who say that such a thing should be carefully documented, it occurs to me that this is probably less of a problem than I at first thought: If someone is using an iterator -- or any other construct -- without knowing what it is supposed to return, he's in for trouble no matter what!
Jay
+1  A: 

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.

Daniel