ConcurrentModificationException in Java
ConcurrentModificationException in Java is an unchecked exception that is thrown when a collection is structurally modified (an element is added or removed) while it is being iterated, other than through the iterator itself. Despite its name, it usually has nothing to do with threads: a single loop that removes from the list it's looping over is enough to cause it. In this lesson, you will learn why it happens and the correct ways to avoid it.
Example: The Exception
Let's see an example that removes an element inside a for-each loop.
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
public class Cme {
public static void main(String[] args) {
List<String> names = new ArrayList<>(
List.of("Navika", "Srihitha", "Advitha"));
try {
for (String name : names) {
if (name.equals("Navika")) {
names.remove(name);
}
}
} catch (ConcurrentModificationException e) {
System.out.println("Caught ConcurrentModificationException");
}
System.out.println(names);
}
}Caught ConcurrentModificationException
[Srihitha, Advitha]Explanation: The for-each loop uses an iterator behind the scenes. names.remove(name) changes the list without telling that iterator. On the next step, the iterator notices that the list was changed behind its back and throws ConcurrentModificationException. Note that "Navika" was already removed before the exception.
Why It Happens
Every ArrayList keeps a counter named modCount that increases on every structural change. When an iterator is created, it saves the current value. Each next() checks whether the counter has changed. If it has, the iterator fails fast with the exception, instead of continuing with an unpredictable result.
A Surprising Case: No Exception at All
Removing the second-to-last element doesn't throw:
import java.util.ArrayList;
import java.util.List;
public class Silent {
public static void main(String[] args) {
List<String> letters = new ArrayList<>(List.of("a", "b", "c"));
for (String s : letters) {
if (s.equals("b")) {
letters.remove(s);
}
}
System.out.println(letters);
}
}[a, c]Explanation: After "b" is removed, the list has 2 elements, and the iterator has already returned 2. So hasNext() returns false, the loop ends, and the check in next() never runs. "c" was never visited. The code looks correct, but it's just as wrong; it only failed silently.
Four Correct Ways
| Way | Example |
|---|---|
removeIf | names.removeIf(n -> n.equals("Navika")); |
Iterator's remove | it.remove(); inside an iterator loop (see "Iterator and ListIterator in Java") |
| Collect, then remove | Gather items in a second list, then call names.removeAll(toRemove) after the loop |
| Loop over a copy | for (String n : new ArrayList<>(names)) { names.remove(n); } |
import java.util.ArrayList;
import java.util.List;
public class Fixed {
public static void main(String[] args) {
List<String> names = new ArrayList<>(
List.of("Navika", "Srihitha", "Advitha"));
names.removeIf(n -> n.equals("Navika"));
System.out.println(names);
}
}[Srihitha, Advitha]Common Mistakes
Assuming the exception means a threading bug. In most cases it's a single loop that changes its own collection.
Trusting code that "works" because it didn't throw. It may be skipping elements.
Catching the exception and continuing. Fix the loop instead.
Interview Corner
Why does ConcurrentModificationException occur?
It occurs when a collection is structurally modified while being iterated by means other than the iterator itself, for example calling list.remove inside a for-each loop over that list. Fail-fast iterators compare the collection's modification count with the value they saved, and throw the exception when they differ. It can happen in a single thread.
How can you remove elements from a list while iterating over it?
Use Iterator.remove(), use Collection.removeIf(condition), collect the elements to remove and call removeAll after the loop, or iterate over a copy of the list. For lists shared between threads, use a concurrent collection.
Practice
- Easy: Rewrite the
Cmeexample with an explicitIteratorandit.remove(). - Harder: Write a loop that adds the upper-case version of each name to the same list. Observe the exception, then fix it by collecting new names in a separate list first.
Key Takeaways
Was this tutorial helpful?