# How to Fix Java ConcurrentModificationException
Your application throws this error while iterating over a collection:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at com.myapp.DataProcessor.removeInvalidItems(DataProcessor.java:28)
at com.myapp.Main.main(Main.java:12)This exception occurs when you modify a collection while iterating over it. The iterator detects that the collection has been modified outside of its control and fails fast.
Understanding the Problem
Java collections use a "modification count" (modCount) to track structural changes. When an iterator is created, it records the current modCount. On each next() call, it checks if the collection's modCount still matches - if not, it throws ConcurrentModificationException.
```java List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
// This WILL cause ConcurrentModificationException for (String item : items) { if (item.equals("b")) { items.remove(item); // Modifies list during iteration! } } ```
Diagnosis Steps
Step 1: Identify the Modification Point
Look at your iteration code for any collection modifications:
// Common mistake patterns
for (Item item : items) {
items.remove(item); // Direct removal
items.add(newItem); // Direct addition
items.clear(); // Clearing
items.sort(comparator); // Sorting modifies structure
}Step 2: Check for Nested Iterations
// Nested iterations can cause issues too
for (Item item1 : items) {
for (Item item2 : items) { // Iterating same collection
if (condition) {
items.remove(item1); // Modifies during outer iteration
}
}
}Step 3: Check for Lambda Side Effects
// Lambda that modifies the source collection
items.stream()
.filter(item -> {
if (item.isInvalid()) {
items.remove(item); // Dangerous!
return false;
}
return true;
})
.collect(Collectors.toList());Solutions
Solution 1: Use Iterator.remove()
The correct way to remove while iterating:
```java List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
// Using iterator directly Iterator<String> iterator = items.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (item.equals("b")) { iterator.remove(); // Safe removal through iterator } } ```
For maps:
```java Map<String, Integer> map = new HashMap<>(); map.put("a", 1); map.put("b", 2);
// Using iterator on entrySet Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() < 0) { iterator.remove(); } } ```
Solution 2: Remove After Collection
Collect items to remove, then remove all at once:
```java List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
// Collect items to remove List<String> toRemove = new ArrayList<>(); for (String item : items) { if (item.equals("b") || item.equals("c")) { toRemove.add(item); } }
// Remove all at once items.removeAll(toRemove);
// Or using removeIf (Java 8+) items.removeIf(item -> item.equals("b") || item.equals("c")); ```
Solution 3: Use removeIf() (Java 8+)
The cleanest approach for conditional removal:
```java List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
// Single line removal items.removeIf(item -> item.equals("b"));
// More complex conditions items.removeIf(item -> item.startsWith("x") || item.length() > 10); ```
Solution 4: Use CopyOnWriteArrayList
For concurrent modification scenarios:
```java import java.util.concurrent.CopyOnWriteArrayList;
List<String> items = new CopyOnWriteArrayList<>(Arrays.asList("a", "b", "c"));
// Safe to modify during iteration for (String item : items) { if (item.equals("b")) { items.remove(item); // Works! Creates a copy internally } } ```
Note: CopyOnWriteArrayList creates a new copy on every modification. Use only for read-heavy, write-rare scenarios.
Solution 5: Create a New Collection
Instead of modifying, create a filtered collection:
```java List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
// Create new list without "b" List<String> filtered = items.stream() .filter(item -> !item.equals("b")) .collect(Collectors.toList());
// Or with traditional loop List<String> filtered = new ArrayList<>(); for (String item : items) { if (!item.equals("b")) { filtered.add(item); } } ```
Solution 6: Fix Concurrent Access
For truly concurrent access from multiple threads:
```java import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ConcurrentLinkedQueue;
// Thread-safe map Map<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.forEach((k, v) -> { map.put(k, v + 1); // Safe });
// Thread-safe list (for read-heavy) List<String> list = new CopyOnWriteArrayList<>();
// Thread-safe queue (for write-heavy) Queue<String> queue = new ConcurrentLinkedQueue<>(); ```
Solution 7: Use Synchronized Blocks
For complex operations requiring atomicity:
```java List<String> items = Collections.synchronizedList(new ArrayList<>());
// WRONG - still causes CME for (String item : items) { items.remove(item); }
// RIGHT - synchronized block synchronized (items) { Iterator<String> iterator = items.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (shouldRemove(item)) { iterator.remove(); } } } ```
Common Scenarios and Fixes
Scenario: Removing Items While Filtering
```java // Problem List<User> users = getUsers(); for (User user : users) { if (user.isInactive()) { users.remove(user); // CME! } }
// Solution 1: removeIf users.removeIf(User::isInactive);
// Solution 2: Stream filter users = users.stream() .filter(user -> !user.isInactive()) .collect(Collectors.toList()); ```
Scenario: Modifying Map During Iteration
```java // Problem Map<String, Integer> map = getMap(); for (Map.Entry<String, Integer> entry : map.entrySet()) { if (entry.getValue() < 0) { map.remove(entry.getKey()); // CME! } }
// Solution: Iterator Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Integer> entry = it.next(); if (entry.getValue() < 0) { it.remove(); } }
// Solution: ConcurrentHashMap Map<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", -1); map.put("b", 2); for (Map.Entry<String, Integer> entry : map.entrySet()) { if (entry.getValue() < 0) { map.remove(entry.getKey()); // Safe with ConcurrentHashMap } } ```
Scenario: Nested Loops Over Same Collection
```java // Problem List<Item> items = getItems(); for (Item outer : items) { for (Item inner : items) { // Same collection if (outer.conflictsWith(inner)) { items.remove(outer); // CME! } } }
// Solution: Collect to remove later Set<Item> toRemove = new HashSet<>(); for (int i = 0; i < items.size(); i++) { for (int j = i + 1; j < items.size(); j++) { if (items.get(i).conflictsWith(items.get(j))) { toRemove.add(items.get(i)); toRemove.add(items.get(j)); } } } items.removeAll(toRemove); ```
Verification
Test your modifications:
```java import org.junit.jupiter.api.Test; import java.util.*; import static org.junit.jupiter.api.Assertions.*;
class CollectionModificationTest {
@Test void removeWhileIterating_noException() { List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
assertDoesNotThrow(() -> { items.removeIf(item -> item.equals("b")); });
assertEquals(Arrays.asList("a", "c"), items); }
@Test void concurrentModification_throwsException() { List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
assertThrows(ConcurrentModificationException.class, () -> { for (String item : items) { items.remove(item); } }); }
@Test void iteratorRemove_noException() { List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> it = items.iterator(); while (it.hasNext()) { if (it.next().equals("b")) { it.remove(); } }
assertEquals(Arrays.asList("a", "c"), items); } } ```
Quick Reference
| Scenario | Problem | Solution |
|---|---|---|
| Remove during for-each | CME thrown | Use iterator.remove() |
| Remove during for-each | CME thrown | Use removeIf() |
| Modify in lambda | CME thrown | Collect then filter |
| Concurrent thread access | CME thrown | Use concurrent collections |
| Nested iteration | CME thrown | Use indices or collect items to remove |
| Complex atomic operation | Race condition | Synchronize on collection |
The golden rule: never modify a collection while iterating over it with a for-each loop. Use iterator's remove() method, removeIf(), or collect changes and apply after iteration.