Provide a way to access elements of a collection sequentially without exposing its underlying representation.
Iterator extracts the traversal logic from a collection into a separate iterator object. Clients use a uniform interface (hasNext(), next()) to traverse any collection — whether it's a list, tree, graph, or database result set — without knowing the internal structure.
Multiple iterators can traverse the same collection independently and simultaneously, each maintaining their own position.
You have a tree data structure. Clients want to traverse it depth-first sometimes and breadth-first other times. Adding both traversal modes directly to the tree bloats the collection class and forces clients to know about traversal internals.
Iterator extracts each traversal strategy into its own iterator class. The tree just provides createDepthFirstIterator() and createBreadthFirstIterator() — the traversal logic lives outside.
hasNext(), next(), optionally remove(). Clients only use this interface.createIterator() so clients can get an iterator without knowing the concrete collection type.createIterator() to return a ConcreteIterator appropriate for its internal structure.// Custom Iterator interface public interface Iterator<T> { boolean hasNext(); T next(); } // The collection public class UserCollection implements Iterable<String> { private final List<String> users = new ArrayList<>(); public void add(String user) { users.add(user); } // Forward iterator public java.util.Iterator<String> iterator() { return new ForwardIterator(); } // Reverse iterator — extra traversal order public java.util.Iterator<String> reverseIterator() { return new ReverseIterator(); } // Filter iterator — only users matching a predicate public java.util.Iterator<String> filterIterator(Predicate<String> pred) { return new FilterIterator(pred); } // ── ConcreteIterator: Forward ────────────────────────── private class ForwardIterator implements java.util.Iterator<String> { private int index = 0; public boolean hasNext() { return index < users.size(); } public String next() { if (!hasNext()) throw new NoSuchElementException(); return users.get(index++); } } // ── ConcreteIterator: Reverse ────────────────────────── private class ReverseIterator implements java.util.Iterator<String> { private int index = users.size() - 1; public boolean hasNext() { return index >= 0; } public String next() { if (!hasNext()) throw new NoSuchElementException(); return users.get(index--); } } // ── ConcreteIterator: Filter ─────────────────────────── private class FilterIterator implements java.util.Iterator<String> { private final Predicate<String> pred; private int index = 0; private String next = null; FilterIterator(Predicate<String> pred) { this.pred = pred; advance(); } private void advance() { next = null; while (index < users.size()) { String candidate = users.get(index++); if (pred.test(candidate)) { next = candidate; break; } } } public boolean hasNext() { return next != null; } public String next() { String r = next; advance(); return r; } } } // Client — uniform traversal regardless of iterator type public class Main { public static void main(String[] args) { UserCollection users = new UserCollection(); users.add("Alice"); users.add("Bob"); users.add("Charlie"); // for-each uses iterator() automatically for (String u : users) System.out.print(u + " "); // Alice Bob Charlie // Reverse var rev = users.reverseIterator(); while (rev.hasNext()) System.out.print(rev.next() + " "); // Charlie Bob Alice // Filtered — names starting with 'A' var filtered = users.filterIterator(u -> u.startsWith("A")); while (filtered.hasNext()) System.out.print(filtered.next()); // Alice } }
java.util.Iterator — the canonical Iterator interface, implemented by every Java Collectionjava.util.ListIterator — bidirectional iterator extending the base IteratorResultSet in JDBC — iterates over DB rows with next() without knowing the DB structureSpliterator is an advanced parallel-capable iterator under the hoodScanner — iterates tokens over input streams via hasNext()/next()ConcurrentModificationExceptionConcurrentModificationException, but custom iterators may silently skip or duplicate elementsNoSuchElementException or returns null depending on implementationnext() on the same iterator instance — both advance the position counter, causing elements to be skippedremove() on an iterator that doesn't support it throws UnsupportedOperationException at runtime with no compile-time warningnext() and throw ConcurrentModificationException if changed (Java's approach)IllegalStateException with a message rather than a bare NPECopyOnWriteArrayList or take a snapshot for concurrent scenariosnew ArrayList<>(original)) when modifications are expected during traversalStream.limit(n) or add a max-elements guard in hasNext()// ❌ BREAKING — concurrent modification for (String user : users) { if (user.startsWith("A")) users.remove(user); // ❌ ConcurrentModificationException } // ✅ FIX 1 — use iterator's own remove() var it = users.iterator(); while (it.hasNext()) { if (it.next().startsWith("A")) it.remove(); // ✅ safe } // ✅ FIX 2 — removeIf (Java 8+) users.removeIf(u -> u.startsWith("A")); // ✅ cleanest approach // ✅ modCount guard in custom iterator private class SafeIterator implements java.util.Iterator<String> { private int index = 0; private final int expectedModCount = modCount; // snapshot at creation private void checkModification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } public boolean hasNext() { checkModification(); return index < items.size(); } public String next() { checkModification(); return items.get(index++); } } // ❌ BREAKING — next() without hasNext() while (true) { System.out.println(it.next()); // ❌ NoSuchElementException when empty } // ✅ FIX — always guard with hasNext() while (it.hasNext()) { System.out.println(it.next()); // ✅ safe }
hasNext()/next(). Multiple iterators can traverse the same collection independently and simultaneously.java.util.Iterator<T> with hasNext(), next(), remove(). Implement Iterable<T> on your collection to enable for-each syntax. Java's iterators are fail-fast — they throw ConcurrentModificationException if collection changes during iteration.iterator.remove() or Collection.removeIf() instead.