The Error
You're looping over a collection and removing (or adding) elements mid-loop โ and Java slams the brakes:
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.example.MyClass.processItems(MyClass.java:24)
Nine times out of ten, it's a for-each loop where you call list.remove(), list.add(), or map.put() directly on the collection you're iterating.
Root Cause
Most Java collections โ ArrayList, HashMap, HashSet โ maintain an internal counter called modCount. It increments every time you structurally change the collection (add, remove, clear).
When you start iterating, the iterator snapshots that counter. On every next() call, it asks: did the collection change since I started? If yes, it throws immediately.
This is the fail-fast design โ better to blow up loudly than silently skip elements or get stuck in an infinite loop.
Classic example:
List<String> items = new ArrayList<>(List.of("a", "b", "c", "remove-me"));
for (String item : items) {
if (item.equals("remove-me")) {
items.remove(item); // throws ConcurrentModificationException
}
}
Fix 1: Use Iterator's remove()
The iterator has its own remove() method โ that's what you should use. It updates modCount internally, so the check passes cleanly.
List<String> items = new ArrayList<>(List.of("a", "b", "c", "remove-me"));
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.equals("remove-me")) {
iterator.remove(); // safe
}
}
System.out.println(items); // [a, b, c]
Limitation: You can only remove the current element. Adding elements or removing others mid-iteration still isn't possible here.
Fix 2: Use removeIf() (Java 8+)
For simple filtering, removeIf() is your best friend. One line, no boilerplate:
List<String> items = new ArrayList<>(List.of("a", "b", "c", "remove-me"));
items.removeIf(item -> item.equals("remove-me"));
System.out.println(items); // [a, b, c]
Works on ArrayList, HashSet, LinkedList โ any Collection that implements the method. Iterator management is handled under the hood.
Fix 3: Collect Changes, Apply After the Loop
Need to add elements, or do something more involved? Don't touch the collection inside the loop. Accumulate your changes separately, then apply them once iteration is done.
List<String> items = new ArrayList<>(List.of("a", "b", "c", "remove-me"));
List<String> toRemove = new ArrayList<>();
for (String item : items) {
if (item.equals("remove-me")) {
toRemove.add(item);
}
}
items.removeAll(toRemove);
System.out.println(items); // [a, b, c]
The same idea works for additions โ accumulate into a toAdd list and call items.addAll(toAdd) afterward:
List<String> toAdd = new ArrayList<>();
for (String item : items) {
if (item.startsWith("prefix-")) {
toAdd.add(item + "-processed");
}
}
items.addAll(toAdd);
Fix 4: Use CopyOnWriteArrayList for Concurrent Access
Dealing with multiple threads hitting the same list? That's a different problem. CopyOnWriteArrayList from java.util.concurrent was built for exactly this:
import java.util.concurrent.CopyOnWriteArrayList;
List<String> items = new CopyOnWriteArrayList<>(List.of("a", "b", "c", "remove-me"));
for (String item : items) {
if (item.equals("remove-me")) {
items.remove(item); // safe โ iterates a snapshot copy
}
}
System.out.println(items); // [a, b, c]
Trade-off: Every write creates a fresh copy of the underlying array. On a list of 10,000 elements with frequent writes, that overhead adds up fast. Only reach for this when true concurrent access is the actual requirement โ not just for convenience.
Fix for Maps
HashMap throws the same exception under the same conditions. The fixes mirror what we did for lists โ use entrySet().removeIf() or an explicit iterator:
Map<String, Integer> scores = new HashMap<>();
scores.put("alice", 90);
scores.put("bob", 45);
scores.put("charlie", 30);
// Remove entries where score < 50
scores.entrySet().removeIf(entry -> entry.getValue() < 50);
System.out.println(scores); // {alice=90}
// Or with iterator
Iterator<Map.Entry<String, Integer>> it = scores.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() < 50) {
it.remove();
}
}
Verification
Run your code and check three things:
- No
ConcurrentModificationExceptionin the stack trace. - The collection has exactly the elements you expect after the loop.
- Print it to confirm:
System.out.println(items);
In multi-threaded code, add a test that hammers the list from multiple threads at once. A quick assertion works well as a smoke test:
// Run and expect no exception
List<String> result = new ArrayList<>(List.of("keep", "remove", "keep2"));
result.removeIf(s -> s.equals("remove"));
assert result.equals(List.of("keep", "keep2")) : "Unexpected result: " + result;
Which Fix to Choose?
- Removing elements only:
removeIf()โ fewest lines, clearest intent. - Custom logic per element:
Iterator.remove(). - Adding or complex modifications: Collect changes in a separate list, apply after the loop.
- True concurrent threads:
CopyOnWriteArrayListโ or synchronize externally if you need finer control.

