Iterator vs ListIterator vs Spliterator
MediumIterator walks any collection forward and can remove. ListIterator is List-only: it moves both ways, knows indexes, and can set or add. Spliterator (Java 8) can split itself in two, which is what lets streams hand chunks of a collection to parallel workers.
How it works
- Iterator has
hasNext(),next(), optionalremove()andforEachRemaining(). EveryCollectionprovides one. - ListIterator extends
Iteratorfor lists. It addshasPrevious(),previous(),nextIndex(),previousIndex(),set(e)to replace the last returned element andadd(e)to insert at the cursor.list.listIterator(i)starts at any index. - Spliterator ("splittable iterator") is built for streams:
tryAdvance(action)processes one element if there is one.trySplit()hands off roughly half of the remaining elements as a newSpliterator, or returnsnullif it can't split.estimateSize()andcharacteristics()(ORDERED,SIZED,SUBSIZED,SORTED,DISTINCT,NONNULL,IMMUTABLE,CONCURRENT) let the stream pipeline plan its work.
- Parallel streams call
trySplit()repeatedly to build a tree of chunks, then process the leaves on the commonForkJoinPool.
Example
List<String> words = new ArrayList<>(List.of("tea", "cofee", "juice"));
// ListIterator: fix a typo in place and insert after it
ListIterator<String> li = words.listIterator();
while (li.hasNext()) {
if (li.next().equals("cofee")) {
li.set("coffee");
li.add("cocoa"); // inserted after "coffee"
}
}
// [tea, coffee, cocoa, juice]
// Walk backwards from the end
for (ListIterator<String> back = words.listIterator(words.size()); back.hasPrevious(); ) {
System.out.print(back.previous() + " ");
}
// Spliterator: split once and see both halves
Spliterator<String> right = words.spliterator();
Spliterator<String> left = right.trySplit();
left.forEachRemaining(w -> System.out.println("L " + w)); // tea, coffee
right.forEachRemaining(w -> System.out.println("R " + w)); // cocoa, juiceEdge cases
ListIterator.set()andremove()throwIllegalStateExceptionif called beforenext()/previous()or right afteradd().ArrayList's spliterator binds late (it reads the list's size at first use, not at creation) and is fail-fast like its iterator.- A spliterator that reports
SIZED | SUBSIZEDlets a stream size arrays and split evenly; aLinkedListsplits poorly because finding the middle means walking it. - To stream your own source, wrap a spliterator:
StreamSupport.stream(spl, parallel).
Common mistakes
- Calling
remove()on the list while holding anIterator. Use the iterator'sremove(). - Saying
Spliteratoris "just a parallel iterator". It also works sequentially; splitting is optional. - Forgetting
ListIterator's cursor sits between elements, sonextIndex()andpreviousIndex()differ by one.
Likely follow-up
"Why does a parallel stream over a LinkedList often run slower than sequential?" Its spliterator can't jump to the middle, so splits are uneven and expensive. An ArrayList or array splits in O(1) by index.
Get every deep dive in the app
Coming soon to the App StoreComing soon to Google Play