All topics
library
intermediate

Iterator, ListIterator & Spliterator

Understand the three iteration interfaces, their capabilities, and when Spliterator enables parallel processing.

Three interfaces for walking a collection, with increasing capability.

Iterator = a bookmark in a book (forward only). ListIterator = a bookmark you can move both ways. Spliterator = tearing the book in half so two people can read simultaneously.

Key Concepts

1
    Iterator       forward only            hasNext, next, remove
    ListIterator   both directions, Lists  + previous, set, add, nextIndex
    Spliterator    splittable, for streams + trySplit, tryAdvance
2
Iterator is what the for-each loop uses.
    for (String s : list) { ... }
Iterator
3
    // compiles to roughly
    Iterator<String> it = list.iterator();
    while (it.hasNext()) { String s = it.next(); ... }
4
Which is why for-each works on a Set and a Queue, where an indexed loop has nothing to index.
SetQueue
5
Removing during iteration must go through the iterator.
    for (String s : list)
      if (s.isEmpty()) list.remove(s);        // ConcurrentModificationException
6
    Iterator<String> it = list.iterator();
    while (it.hasNext())
      if (it.next().isEmpty()) it.remove();   // correct
7
    list.removeIf(String::isEmpty);           // better
8
ListIterator adds backwards movement and replacement:
    ListIterator<String> it = list.listIterator();
    while (it.hasNext()) {
      String s = it.next();
      it.set(s.trim());            // replace in place
      if (s.isBlank()) it.add("?"); // insert after the cursor
    }
ListIterator
9
It only exists for List, because the other collections have no position to go back to.
List
10
Spliterator exists so streams can go parallel.
    trySplit()           hand off half the elements to another thread
    tryAdvance(action)   process one element
    forEachRemaining     process the rest
    characteristics()    SIZED, ORDERED, SORTED, DISTINCT, IMMUTABLE
Spliterator
11
collection.stream() builds one internally, and parallelStream() calls trySplit repeatedly to divide the work.
collection.stream()parallelStream()trySplit
12
Characteristics are what make parallelism efficient. An ArrayList reports SIZED and splits exactly in half in constant time. A LinkedList cannot, so it splits badly and parallel streams over one rarely pay off. That is the practical consequence of the interface.
ArrayListSIZEDLinkedList
13
Fail-fast is best effort. Iterator throws ConcurrentModificationException when it notices a structural change, but it is not guaranteed to notice. Never write code that catches it.
IteratorConcurrentModificationException
14
What the interviewer is probing.1. "How do you remove elements while iterating?" Probing: the safe mechanisms. Stalls: "Call list.remove in the loop." Moves up: removeIf, or iterator.remove — modifying the collection directly throws ConcurrentModificationException.
15
2. "Why does removing the second-to-last element sometimes not throw?" Probing: the hasNext check, which makes the bug look intermittent. Stalls: "It always throws." Moves up: after the removal hasNext compares the cursor against the new smaller size, returns false, and the loop exits before any check runs.
16
3. "What does Spliterator add over Iterator?" Probing: parallelism. Stalls: "Nothing much." Moves up: trySplit, so a stream can divide the work across threads, plus characteristics like SIZED and ORDERED that decide how efficiently it splits.
17
4. "Why do parallel streams work poorly over a LinkedList?" Probing: the link between structure and splitting. Stalls: "They work the same." Moves up: it cannot report a size or split in constant time, so the work divides badly — an ArrayList halves exactly.