← Debugging Guides

阅读中文版本

Java — ConcurrentModificationException while iterating

Fix ConcurrentModificationException by removing elements through the iterator or using concurrent collections.

beginnerdebuggingjavacollections

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/*.java

Interpretation

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" src

Interpretation

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