Iterator.hasNext reports whether another element is available, but a custom implementation must not assume it is called only once before next. Enhanced for loops follow that narrow pattern, which can hide an incorrect iterator contract. section 8.13
The stateful hasNext mistake
An iterator that increments its cursor inside hasNext may appear correct in a for-each loop, yet repeated hasNext calls skip elements and next called first returns invalid state. Collections.min and Collections.max can call next before hasNext, including on an empty collection, where next must throw NoSuchElementException. section 8.13
Avoid the failure by following these implementation rules:
- Make repeated hasNext calls stable; cache a prefetched value when reading from a BufferedReader. section 8.13
- Validate availability in next, return the current element, advance afterward, and throw NoSuchElementException when exhausted. section 8.13
- Prefer IntStream.range(...).iterator() or reader.lines().iterator() when those APIs express the intended iteration. section 8.13
public Iterator<Integer> iterator() {
return new Iterator<>() {
int cur = 0;
public boolean hasNext() {
return cur < max;
}
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return cur++;
}
};
}
Use the corrected implementation to inspect stable hasNext behavior, validation in next, cursor advancement, and exhaustion handling. section 8.13
