# How to Fix Java ConcurrentModificationException

Your application throws this error while iterating over a collection:

bash
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:

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

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

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

ScenarioProblemSolution
Remove during for-eachCME thrownUse iterator.remove()
Remove during for-eachCME thrownUse removeIf()
Modify in lambdaCME thrownCollect then filter
Concurrent thread accessCME thrownUse concurrent collections
Nested iterationCME thrownUse indices or collect items to remove
Complex atomic operationRace conditionSynchronize 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.