Writing your own Iterable
Make your own type work in a for-each loop by implementing Iterator correctly.
Open this lesson in the learning hubKey points
- A for-each loop works on anything that implements
Iterable, which is a single method:iterator(). - The compiler rewrites the loop into
hasNext()andnext()calls — there is no magic in it. hasNextmust consume nothing, andnextmust throwNoSuchElementExceptiononce exhausted.- Return a fresh iterator on every call, or a second loop over the same object finds nothing left.
- For a read-only sequence leave
remove()alone: the interface default already throws. - Extending
AbstractListgives youiterator,containsandtoStringfor free.
Example
import java.util.*;
public class Main {
// Iterable is one method. This is everything for-each needs.
static final class Range implements Iterable<Integer> {
private final int from;
private final int toExclusive;
Range(int from, int toExclusive) {
this.from = from;
this.toExclusive = toExclusive;
}
@Override public Iterator<Integer> iterator() {
return new Iterator<>() { // a fresh cursor per call
private int cursor = from;
@Override public boolean hasNext() { return cursor < toExclusive; }
@Override public Integer next() {
if (!hasNext()) throw new NoSuchElementException("range exhausted");
return cursor++;
}
};
}
}
// Two methods, and AbstractList supplies the rest of List.
static final class Trio extends AbstractList<String> {
private final String[] data = {"a", "b", "c"};
@Override public String get(int i) { return data[i]; }
@Override public int size() { return data.length; }
}
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for (int i : new Range(1, 5)) sb.append(i).append(" ");
System.out.println("for-each over Range : " + sb.toString().trim());
Range r = new Range(1, 4);
System.out.println("looped twice : " + count(r) + " then " + count(r) + " (fresh cursor each time)");
Iterator<Integer> it = new Range(1, 2).iterator();
it.next();
try {
it.next();
} catch (NoSuchElementException e) {
System.out.println("next() past the end : NoSuchElementException");
}
try {
new Range(1, 3).iterator().remove();
} catch (UnsupportedOperationException e) {
System.out.println("inherited remove() : UnsupportedOperationException");
}
Trio trio = new Trio();
System.out.println("AbstractList gives : " + trio + " contains(b)=" + trio.contains("b")
+ " indexOf(c)=" + trio.indexOf("c"));
}
static int count(Iterable<Integer> src) {
int n = 0;
for (int ignored : src) n++;
return n;
}
}
for-each is just hasNext and next. Implement those two honestly and your type joins the language.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Collections course, and every lesson in it is listed on the Collections contents page.