15
votes

Since Java 5, we have the new java.lang.Iterable type that can be used in foreach loops as such:

for (Object element : iterable);

The Iterable contract does not specify whether its iterator() method can be called more than once before disposing of the Iterable. I.e., it is not clear whether the following can be expected to work for all Iterables:

for (Object element : iterable);
for (Object element : iterable);

For instance, an Iterator wrapping implementation cannot be used twice:

public class OneShotIterable<T> implements Iterable<T> {
    private final Iterator<T> it;

    public OneShotIterable(Iterator<T> it) {
        this.it = it;
    }

    @Override
    public Iterator<T> iterator() {
        return it;
    }
}

For most Iterables, this is irrelevant, as they are in fact retro-fitted Collection API types such as List, Set, which already have well-defined contracts for their iterator() methods.

My question is: Is my OneShotIterable implementation violating some contract that I'm overlooking? In other words, will users of an Iterable expect it to be reusable? If so, is there an "official" recommendation by the Java 5 expert group how to deal with such "one shot" Iterables (e.g. throw an IllegalStateException on a second call)?

2
Couldn't you just increment a field of your OneShotIterable each time iterator() get called, and throw an exception if it's gte n?sp00m
@LukasEder doh you edited it awayTheBlastOne

2 Answers

16
votes

One precedent that I could find in the standard library is the DirectoryStream interface.

Its Javadoc contains the following passage (emphasis theirs):

While DirectoryStream extends Iterable, it is not a general-purpose Iterable as it supports only a single Iterator; invoking the iterator method to obtain a second or subsequent iterator throws IllegalStateException.

To me, this suggests two things:

  • The implied contract on Iterable is that you're supposed to be able to iterate more than once (perhaps even concurrently!)
  • A boldface warning in the documentation coupled with throwing IllegalStateException is probably the best way to handle non-compliance in your own classes/interfaces.
1
votes

Not really an answer to my question, but Apache Commons Collections has addressed this issue in its IteratorUtils class:

Both are factory methods for the IteratorIterable type. The above distinction makes it clear that one must take care when wrapping Iterators in Iterables.