Java — ConcurrentModificationException while iterating
Fix ConcurrentModificationException by removing elements through the iterator or using concurrent collections.
Symptoms
- java.util.ConcurrentModificationException on a for-each loop
- The error appears even in single-threaded code
- Removing from a collection while iterating throws
- A copy or sublist iteration sees a change
Possible causes
- The loop mutates the collection it iterates instead of using the iterator
- Another thread modifies the collection during iteration
- A nested iteration removes from the outer collection
- A stream or sublist is iterated while the backing collection changes
Diagnosis steps
Find the mutation inside the loop
grep -n "for (\|for (\(.*:\|remove(\|add(" src/*.javaInterpretation
A remove or add inside a for-each over the same collection is the classic trigger.
Use the iterator remove or removeIf
it.remove(); list.removeIf(predicate)Interpretation
Iterator.remove and removeIf mutate safely and preserve fail-fast consistency without throwing.
Confirm thread involvement
grep -rn "Collections.synchronized\|ConcurrentHashMap\|CopyOnWrite" srcInterpretation
If multiple threads share the collection, a concurrent collection or external synchronization is required.
Root cause
The collection was structurally modified while an iterator was active, which fail-fast iterators detect and report rather than continuing with an inconsistent view.
Fix
Remove through Iterator.remove or Collection.removeIf, or iterate over a snapshot copy. For multi-threaded access use a concurrent collection or synchronize the whole iteration.
Verify the fix
Rerun the code and confirm no ConcurrentModificationException, and confirm the collection ends in the intended state.
Symptom walkthrough
A loop throws ConcurrentModificationException, sometimes in single-threaded code where no concurrency is obvious. The name misleads: the cause is structural modification during iteration, which a fail-fast iterator detects and refuses to continue. Removing an element directly from a for-each loop or changing a list while a sublist or stream iterates it both trigger this.
Fix in practice
Remove elements through the iterator or with removeIf, or iterate over a defensive copy when you must mutate the original. If multiple threads truly access the collection, switch to a concurrent collection or synchronize the iteration, but first confirm that concurrency is the actual cause.
Verification
Rerun the code path and confirm the exception is gone, then inspect the collection to confirm it ends in the intended state. An empty result would suggest the fix removed too much.
Diagnostic commands
Check the behavior with commands like these:
grep -n "for (\|for (\(.*:\|remove(\|add(" src/*.java
it.remove(); list.removeIf(predicate)
grep -rn "Collections.synchronized\|ConcurrentHashMap\|CopyOnWrite" src