GotchaDifficulty: Advanced1 min

Why Removing Inside a for-each Throws

A loop that removes the cancelled orders from a list. It throws on the second removal, with an exception whose name mentions concurrency in code that has one thread.

java
for (String ref : refs) {
    if (ref.startsWith("X")) {
        refs.remove(ref);
    }
}
text
Exception in thread "main" java.util.ConcurrentModificationException

The 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.

Three ways out, in order of how often they are the right one.

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