Introduction

ConcurrentModificationException is thrown when a collection is structurally modified while being iterated, except through the iterator's own remove() method. The for-each loop uses an iterator internally, so calling list.remove() inside a for-each loop modifies the collection's modCount without updating the iterator's expected count, triggering the exception.

Symptoms

  • java.util.ConcurrentModificationException at ArrayList$Itr.next()
  • Exception occurs during iteration, not at the actual remove call
  • Works when removing a single element but fails with multiple
  • Non-deterministic behavior depending on which elements are removed

```java // WRONG - throws ConcurrentModificationException List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie")); for (String name : names) { if (name.startsWith("B")) { names.remove(name); // CME on next iteration! } }

// Exception: // java.util.ConcurrentModificationException // at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) // at java.util.ArrayList$Itr.next(ArrayList.java:967) ```

Common Causes

  • Removing elements from a list during for-each iteration
  • Modifying a collection in a stream's forEach lambda
  • Multiple threads accessing the same ArrayList without synchronization
  • Adding elements during iteration (also triggers CME)
  • Nested loops modifying the same collection

Step-by-Step Fix

  1. 1.Use Iterator.remove():
  2. 2.```java
  3. 3.// CORRECT - use iterator's own remove method
  4. 4.List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
  5. 5.Iterator<String> it = names.iterator();
  6. 6.while (it.hasNext()) {
  7. 7.String name = it.next();
  8. 8.if (name.startsWith("B")) {
  9. 9.it.remove(); // Safe - iterator knows about the removal
  10. 10.}
  11. 11.}
  12. 12.// Result: [Alice, Charlie]
  13. 13.`
  14. 14.Use removeIf (Java 8+):
  15. 15.```java
  16. 16.// CORRECT - cleanest approach
  17. 17.List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
  18. 18.names.removeIf(name -> name.startsWith("B"));
  19. 19.// Result: [Alice, Charlie]
  20. 20.`
  21. 21.Collect to a new list:
  22. 22.```java
  23. 23.// CORRECT - create a new filtered list
  24. 24.List<String> names = List.of("Alice", "Bob", "Charlie");
  25. 25.List<String> filtered = names.stream()
  26. 26..filter(name -> !name.startsWith("B"))
  27. 27..collect(Collectors.toList());

// Or with traditional loop List<String> toRemove = new ArrayList<>(); for (String name : names) { if (name.startsWith("B")) { toRemove.add(name); // Collect first, remove later } } names.removeAll(toRemove); // Safe - not iterating ```

  1. 1.Use CopyOnWriteArrayList for concurrent modification:
  2. 2.```java
  3. 3.// For scenarios where modification during iteration is expected
  4. 4.List<String> names = new CopyOnWriteArrayList<>(List.of("Alice", "Bob", "Charlie"));

// Safe to modify during iteration (iterator uses snapshot) for (String name : names) { if (name.startsWith("B")) { names.remove(name); // No CME - iterator uses copy } }

// WARNING: CopyOnWriteArrayList copies the entire array on each write // Only use for read-heavy, write-rarely scenarios ```

  1. 1.Thread-safe iteration with synchronization:
  2. 2.```java
  3. 3.List<String> names = Collections.synchronizedList(new ArrayList<>());

// Must synchronize on the list during iteration synchronized (names) { Iterator<String> it = names.iterator(); while (it.hasNext()) { String name = it.next(); if (shouldRemove(name)) { it.remove(); } } } ```

Prevention

  • Prefer removeIf() for conditional removal - it is both safe and readable
  • Never call list.remove() inside a for-each loop
  • Use CopyOnWriteArrayList only for read-heavy, write-rarely patterns
  • Use ConcurrentHashMap for concurrent map access instead of Collections.synchronizedMap
  • Add unit tests that exercise removal during iteration
  • Use @GuardedBy annotations to document thread safety requirements
  • In Spring, use @Transactional properly to avoid concurrent collection modification in service layers