liveBook · Manning

Iterator

As explained in 100 Java Mistakes and How to Avoid Them

An iterator is an object that traverses the elements of a collection and can optionally remove elements through its iteration protocol.

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

How 100 Java Mistakes and How to Avoid Them covers it

How deep it goes
Explains iterator call contracts, fail-fast modification checks, and corrected range and reader implementations without detailing collection internals beyond modCount.
Examples
Uses a numeric range, buffered reader, conditional list filtering, and list mutation during enhanced for traversal.
What it assumes
Assumes familiarity with Java collections and loops while showing the iterator protocol through focused implementations.
What you can do after
Diagnose/debug with it by avoiding skipped elements, concurrent modifications, and custom iterators that fail outside enhanced for loops.
How it connects
Connects enhanced for loops, Iterable, ConcurrentModificationException, ListIterator, removeIf, streams, and NoSuchElementException.

Also explained in

Compare how 15 books explain iterator →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Java Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.