An iterator traverses a collection through hasNext() and next(). The common enhanced for loop calls hasNext(), then next(), but that narrow pattern is not the full contract. A correct iterator must also tolerate valid callers that invoke next() without first calling hasNext(), call hasNext() repeatedly, or reach exhaustion through next(). section 8.13
Keep advancement in next()
A range iterator is broken when hasNext() increments its cursor: repeated hasNext() calls skip values, while Collections.min() may call next() first and receive the wrong value. Keep hasNext() as a state check; let next() verify availability, throw NoSuchElementException when exhausted, return the current value, and advance the cursor. section 8.13
For an iterator backed by a reader, hasNext() may need to fetch ahead, but it must cache that value. Repeated checks must not consume multiple lines; next() returns the cached value and clears the cache. When possible, use IntStream.range(...).iterator() or reader.lines().iterator() instead of implementing this protocol directly. section 8.13
Do not structurally modify an ordinary mutable collection while traversing it, except through the iterator performing the traversal. Standard collections commonly use fail-fast detection: an iterator compares its saved modification count with the collection and may throw ConcurrentModificationException on a later next() call. Detection is not a guarantee that every illegal modification is caught. section 8.8 section 8.14
Choose removal approaches by the operation needed:
- Use iterator.remove() when a loop does additional work and conditionally removes its current element; this avoids skipping the shifted next list element. section 8.10
- Use removeIf() when removal depends only on a condition rather than an index. section 8.10
- Use subList(...).clear() for an unconditional suffix removal, after checking bounds; reverse indexed traversal is another option when index-based logic is required. section 8.10
return new Iterator<>() {
String next;
public boolean hasNext() {
if (next != null) return true;
try {
next = reader.readLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return next != null;
}
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
String cur = next;
next = null;
return cur;
}
};
A buffered-reader iterator caches look-ahead state, makes repeated hasNext() calls safe, and enforces exhaustion in next(). section 8.13














