Explain Codes LogoExplain Codes Logo

Why is Java's Iterator not an Iterable?

java
iterator-pattern
best-practices
java-8
Alex KataevbyAlex Kataev·Nov 10, 2024
TLDR

Iterable is an interface providing multiple, independent iterators over a data source, while an Iterator encapsulates just one traversal through the data. To make an Iterator compatible with a for-each loop, you can wrap it in an Iterable:

public class SingleUseIterable<T> implements Iterable<T> { private Iterator<T> originalIterator; public SingleUseIterable(Iterator<T> iterator) { this.originalIterator = Objects.requireNonNull(iterator, "Iterator can't be null, unless you like NPE"); } @Override public Iterator<T> iterator() { if (originalIterator == null) { throw new IllegalStateException("Oops! Someone already walked this path."); } Iterator<T> tempIterator = this.originalIterator; this.originalIterator = null; // Single-use ticket to Iterator land return tempIterator; } } // Skinny dipping in Iterables Iterable<String> iterableWrapper = new SingleUseIterable<>(myIterator); for (String s : iterableWrapper) { // Process s and enjoy the Iterable spree }

Now, let's dissect the Iterator vs. Iterable dichotomy to understand the logic behind this.

Independent states and Iterator's one-way street

An Iterator is stateful - it carries its state, e.g., where it currently points in a collection. This independence between Iterator instances is vital for multithreaded environments. A Iterable, on the other hand, provides a well-defined contract to yield a fresh Iterator for each call to iterator(). This ensures that multiple threads can concurrently traverse a collection without tripping over each other – like runners on their lanes in a race track.

Breaking the Iterator-Iterable impasse

If Iterator were to extend Iterable, it would inherently be tied to its iterator() method. This could severely compromise its independence leading to bizarre and unwanted outcomes. To allow an Iterator in a for-each loop, we tactically adapt it to an Iterable.

Java 8: A big leap for Iterator kind

The Iterator interface got an upgrade with Java 8 in the form of the forEachRemaining() method. This allows the usage of lambda expressions for remaining elements, providing a form of internal iteration that could potentially deliver better performance for collections that can optimize the traversal.

Best Practices: Navigating the Iterator-Iterable maze

Wrapping an Iterator in an Iterable should be a last-resort strategy, applicable in scenarios dealing with legacy APIs and unusual cases where only an Iterator is available. Modern Java versions, (thank heavens!) afford versatile alternatives - from the elegant stream API to the enhanced for loop to the forEachRemaining() method. These are, indeed, interesting times to be a Java iterator.