Iterators and Iteration Patterns
Master the art of traversing collections safely and efficiently. From the humble Iterator to the parallel-ready Spliterator, understand how Java's iteration abstractions work under the hood, why ConcurrentModificationException exists, and when parallel streams actually help.
📋 At a Glance
| Aspect | Details |
|---|---|
| Topic | Iterator, ListIterator, Spliterator, and iteration patterns |
| Complexity | Intermediate |
| Prerequisites | Part 1 (Framework Architecture), Part 2 (Generics) |
| Time to Master | 3-4 hours |
| Interview Frequency | Very High (fail-fast, ConcurrentModificationException) |
🎯 What You'll Learn
After completing this article, you will be able to:
- Understand Iterator internals and the fail-fast mechanism
- Use ListIterator for bidirectional traversal and modification
- Master Spliterator for parallel processing
- Choose the right iteration pattern for each scenario
- Avoid ConcurrentModificationException in production code
Production Story: The Mysterious ConcurrentModificationException
The Incident
ConcurrentModificationException intermittently, causing transactions to fail. The error rate was climbing: 0.1%, 0.5%, 2%...The stack trace pointed to seemingly innocent code:
JAVA(14 lines)CodeLoading syntax highlighter...
Why It Worked... Sometimes
The code had been in production for months. Why did it suddenly break?
ConcurrentModificationException happens when you modify a collection while iterating with an enhanced for-loop. But here's the catch: the exception isn't guaranteed to be thrown.JAVA(6 lines)CodeLoading syntax highlighter...
modCount (modification count) at each next() call. If it differs from expectedModCount, exception is thrown. But if you remove the last element or certain elements in specific positions, the iteration might complete before the check triggers.Our traffic pattern changed - we started processing more expired payments, which happened to be in the middle of lists rather than at the end.
The Root Cause
Let me show you what happens inside:
JAVA(16 lines)CodeLoading syntax highlighter...
list.remove() during iteration:- The list's
modCountincrements - The iterator's
expectedModCountstays the same - Next
iterator.next()detects the mismatch - Exception thrown!
The Fix
JAVA(34 lines)CodeLoading syntax highlighter...
Impact and Lessons
- 47 failed transactions during the 23-minute incident
- $12,000 in delayed payments
- 3 support escalations
- Never modify collections during enhanced for-loop iteration
- The bug can hide for months before manifesting
- Use
Iterator.remove()orremoveIf()for safe removal - Code review should flag any
collection.remove()inside loops
Mental Model: The Library Reading Room
Think of iteration like reading books in a library reading room:
TEXT(22 lines)CodeLoading syntax highlighter...
Iterator = Reader with Bookmark
- Position tracking: Knows which book is current
- Version checking: Remembers shelf arrangement
- Safe removal: Can return the book they're holding
ConcurrentModificationException = "Shelf Changed!"
- Reader expected 5 books, now there are 4
- Bookmark position might be invalid
- Must restart the reading session
Spliterator = Multiple Readers Sharing Work
TEXT(19 lines)CodeLoading syntax highlighter...
Deep Dive: Iterator Internals
Iterator Interface
JAVA(14 lines)CodeLoading syntax highlighter...
ArrayList Iterator Implementation
JAVA(43 lines)CodeLoading syntax highlighter...
Why Iterator.remove() is Safe
TEXT(21 lines)CodeLoading syntax highlighter...
Iterator.remove() updates expectedModCount to match the new modCount, keeping them synchronized.Deep Dive: ListIterator
ListIterator extends Iterator with bidirectional traversal and modification:JAVA(14 lines)CodeLoading syntax highlighter...
ListIterator Cursor Position
TEXT(14 lines)CodeLoading syntax highlighter...
ListIterator Usage Examples
JAVA(33 lines)CodeLoading syntax highlighter...
Deep Dive: Spliterator
Spliterator (split-iterator) is designed for parallel traversal:JAVA(21 lines)CodeLoading syntax highlighter...
Spliterator Characteristics
JAVA(11 lines)CodeLoading syntax highlighter...
Collection Spliterator Characteristics
JAVA(19 lines)CodeLoading syntax highlighter...
How trySplit() Works
JAVA(19 lines)CodeLoading syntax highlighter...
ArrayList Spliterator Implementation
JAVA(35 lines)CodeLoading syntax highlighter...
Deep Dive: Parallel Stream and Spliterator
When Parallel Streams Help
JAVA(23 lines)CodeLoading syntax highlighter...
Spliterator Quality Impact
JAVA(8 lines)CodeLoading syntax highlighter...
Custom Spliterator Example
JAVA(38 lines)CodeLoading syntax highlighter...
Iteration Patterns Comparison
Pattern 1: Enhanced For-Loop
JAVA(7 lines)CodeLoading syntax highlighter...
Pattern 2: Iterator
JAVA(8 lines)CodeLoading syntax highlighter...
Pattern 3: ListIterator
JAVA(11 lines)CodeLoading syntax highlighter...
Pattern 4: Index-Based Loop
JAVA(7 lines)CodeLoading syntax highlighter...
Pattern 5: forEach Method
JAVA(6 lines)CodeLoading syntax highlighter...
Pattern 6: Stream API
JAVA(10 lines)CodeLoading syntax highlighter...
Pattern 7: removeIf
JAVA(11 lines)CodeLoading syntax highlighter...
Pattern 8: replaceAll
JAVA(9 lines)CodeLoading syntax highlighter...
Decision Matrix
TEXT(27 lines)CodeLoading syntax highlighter...
⚠️ Common Mistakes
Mistake 1: Modifying Collection in Enhanced For-Loop
JAVA(17 lines)CodeLoading syntax highlighter...
Mistake 2: Calling remove() Without next()
JAVA(20 lines)CodeLoading syntax highlighter...
Mistake 3: Using Index Loop with LinkedList
JAVA(10 lines)CodeLoading syntax highlighter...
Mistake 4: Assuming Parallel Stream is Faster
JAVA(16 lines)CodeLoading syntax highlighter...
Mistake 5: Not Checking Iterator State
JAVA(12 lines)CodeLoading syntax highlighter...
🐛 Debug This
Challenge 1: The Silent Skip
JAVA(9 lines)CodeLoading syntax highlighter...
ConcurrentModificationException?[1, 3, 5, 6] or throw ConcurrentModificationException.it.next(), not at numbers.remove(). If removal happens just before the loop would end naturally, the exception might not be thrown.JAVA(2 lines)CodeLoading syntax highlighter...
Challenge 2: The Double Processing
JAVA(11 lines)CodeLoading syntax highlighter...
A B B C[A, X, B, C]next()returns "A", prints "A"next()returns "B", prints "B"previous()moves cursor back (returns "B" but we ignore it)add("X")inserts X before B, cursor moves after Xnext()returns "B" again (we're still before B), prints "B"next()returns "C", prints "C"
Challenge 3: The Parallel Puzzle
JAVA(9 lines)CodeLoading syntax highlighter...
synchronizedList makes individual operations thread-safe, the check-then-act pattern (i % 2 == 0 then add) is not atomic. The add operations are safe, but you might get non-deterministic ordering.add, it should be 5000 (all even numbers from 0 to 9999). But if we had compound operations like check-then-modify, we'd have race conditions.JAVA(6 lines)CodeLoading syntax highlighter...
💻 Exercises
Exercise 1: Implement forEachRemaining
forEachRemaining using only hasNext() and next():JAVA(3 lines)CodeLoading syntax highlighter...
JAVA(12 lines)CodeLoading syntax highlighter...
Exercise 2: Remove Every Nth Element
Write a method that removes every Nth element from a list:
JAVA(8 lines)CodeLoading syntax highlighter...
JAVA(20 lines)CodeLoading syntax highlighter...
Exercise 3: Interleave Two Lists
Create an iterator that interleaves elements from two lists:
JAVA(9 lines)CodeLoading syntax highlighter...
JAVA(35 lines)CodeLoading syntax highlighter...
Exercise 4: Filtered Iterator
Create an iterator that only returns elements matching a predicate:
JAVA(8 lines)CodeLoading syntax highlighter...
JAVA(39 lines)CodeLoading syntax highlighter...
Exercise 5: Peekable Iterator
Create an iterator that allows peeking at the next element without consuming it:
JAVA(12 lines)CodeLoading syntax highlighter...
JAVA(34 lines)CodeLoading syntax highlighter...
🎤 Senior-Level Interview Questions
Question 1: Fail-Fast vs Fail-Safe Iterators
- Throw
ConcurrentModificationExceptionif collection is modified during iteration - Track
modCountto detect modifications - Provide immediate feedback about concurrent modification
- Never throw
ConcurrentModificationException - Work on a copy of the collection or use special locking
- May not reflect modifications made during iteration
JAVA(11 lines)CodeLoading syntax highlighter...
Question 2: Iterator.remove() Internals
Iterator.remove() work but Collection.remove() throws exception during iteration?expectedModCount synchronization:-
Collection.remove(): Increments
modCountbut the iterator'sexpectedModCountstays unchanged. Nextiterator.next()detects mismatch. -
Iterator.remove(): After removing, it sets
expectedModCount = modCount, keeping them synchronized.
JAVA(5 lines)CodeLoading syntax highlighter...
Question 3: Spliterator Characteristics
- SIZED: Exact count known - enables accurate parallel work division
- SUBSIZED: Split portions also have known sizes
- ORDERED: Encounter order is defined - must be preserved in parallel ops
- SORTED: Elements are sorted - enables optimizations
- DISTINCT: No duplicates - enables certain optimizations
- NONNULL: No null elements - can skip null checks
- IMMUTABLE: Source won't change - safe for parallel access
- CONCURRENT: Can be modified during traversal safely
These matter for parallel stream optimization - e.g., if not SIZED, parallel division is harder.
Question 4: Why LinkedList Parallel Streams are Slow
-
Poor Splittability: LinkedList cannot split in O(1) time. To split, it must traverse to the middle - O(n). ArrayList splits by index calculation - O(1).
-
No SUBSIZED characteristic: After splitting, LinkedList doesn't know the exact size of each half without counting, making work distribution uneven.
JAVA(5 lines)CodeLoading syntax highlighter...
Question 5: ListIterator vs Iterator
- Bidirectional traversal:
previous(),hasPrevious() - Element replacement:
set(E e) - Element insertion:
add(E e) - Index information:
nextIndex(),previousIndex()
JAVA(14 lines)CodeLoading syntax highlighter...
Question 6: forEachRemaining vs forEach
Iterator.forEachRemaining() and Iterable.forEach()?- Processes remaining elements in the iterator
- Iterator position matters - starts from current position
- Can be used after manually processing some elements
- One-shot - iterator is exhausted after call
- Processes all elements from the beginning
- Uses internal iteration (collection controls iteration)
- Can be called multiple times
- Default implementation creates new iterator each time
JAVA(11 lines)CodeLoading syntax highlighter...
📝 Summary & Key Takeaways
Iterator Essentials
IteratorprovideshasNext(),next(),remove()- Fail-fast iterators track
modCountto detect concurrent modification - Always use
Iterator.remove()for safe removal during iteration forEachRemaining()processes remaining elements in bulk
ListIterator Features
- Bidirectional:
previous(),hasPrevious() - Modification:
set(),add() - Position:
nextIndex(),previousIndex() - Cursor is between elements, not on them
Spliterator for Parallelism
trySplit()divides work for parallel processing- Characteristics describe source properties
- SIZED + SUBSIZED = efficient parallel splitting
- ArrayList splits well, LinkedList doesn't
Iteration Pattern Selection
- Enhanced for-loop: Simple read-only traversal
- Iterator: Need to remove during iteration
- ListIterator: Need bidirectional or modification
- removeIf/replaceAll: Bulk conditional operations
- Stream: Transformation and aggregation
🏁 Conclusion
Iterators are deceptively simple on the surface but reveal sophisticated design when you dig deeper. The fail-fast mechanism protects you from subtle bugs, the ListIterator enables complex traversal patterns, and Spliterator bridges the gap to parallel processing.
Key takeaways:
- Never modify collections during enhanced for-loop - use Iterator or removeIf
- Understand modCount/expectedModCount - it's the foundation of fail-fast
- ListIterator cursor is between elements - add() inserts at cursor position
- Spliterator characteristics matter - they determine parallel efficiency
- Not all collections parallelize well - ArrayList yes, LinkedList no
In the next article, we'll explore Arrays and BitSet - the primitive building blocks that underlie many collection implementations.
📅 Review Schedule
To solidify your understanding, review this material:
- Tomorrow: Re-read the Iterator.remove() internals section
- In 3 days: Implement Exercise 4 (FilteredIterator) again without looking
- In 1 week: Explain Spliterator characteristics to a colleague
- In 2 weeks: Review the production story and common mistakes