Card 06/ 08
ConcurrentModificationException: Removing Inside a for-each Loop
A single-threaded loop that removes cancelled orders, and an exception that mentions concurrency.
Removing an element through the list while an enhanced for loop is walking it usually throws ConcurrentModificationException, even with a single thread. The loop uses an iterator (the object that walks a collection one element at a time), and the iterator detects that the list changed underneath it and refuses to continue.
A loop that removes the cancelled orders, whose references start with X, from a list:
for (String ref : refs) {
if (ref.startsWith("X")) {
refs.remove(ref);
}
}Exception in thread "main" java.util.ConcurrentModificationExceptionThe enhanced for uses an iterator, and the iterator keeps a count of how many times the list has been structurally changed. Removing through the list bumps that count without telling the iterator, so the next step notices the mismatch and refuses to continue.
There are three ways out, in order of how often each is the right one:
refs.removeIf(ref -> ref.startsWith("X"));
Iterator<String> it = refs.iterator();
while (it.hasNext()) {
if (it.next().startsWith("X")) { it.remove(); }
}
List<String> kept = refs.stream().filter(ref -> !ref.startsWith("X")).toList();removeIf is one line and says what it means. The iterator's own remove is the general answer when the condition needs more than an expression. Building a new list is the right answer when the original should not change at all.
← → move through sections and cards