Java DISCUSSION

ConcurrentModificationException when removing from a Java list inside a for-each loop

Started by amulya ConcurrentModificationExceptionfail-fast iteratorremoveIfIterator removeJava collections
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

ConcurrentModificationException when removing from a Java list inside a for-each loop

amulya Java Forum
#1

I loop over a list of sensor objects and drop the ones that have timed out: for (Sensor s : sensors) { if (s.isStale()) sensors.remove(s); }. This throws java.util.ConcurrentModificationException, although the program has only one thread. Even stranger, when the stale sensor happens to be the second-to-last element, no exception is thrown and the last element is silently skipped.

What does "concurrent" mean here if no other thread is involved, why is the behaviour inconsistent, and what is the right way to remove elements while iterating?

Community replies 5

Re: ConcurrentModificationException when removing from a Java list inside a for-each loop

#2

"Concurrent" here means modified while an iteration is in progress, not modified by another thread. A for-each loop is compiled into calls on an Iterator: hasNext() and next(). ArrayList keeps a counter, modCount, that every structural change increments. The iterator remembers the count it started with, and next() compares the two and throws if they differ.

The check exists because once an element has been removed underneath it, the iterator's position no longer matches the list, and it would skip or repeat elements. An exception is the polite alternative to silently wrong results.

Re: ConcurrentModificationException when removing from a Java list inside a for-each loop

#3

The second-to-last anomaly follows from where the check sits: in next(), not in hasNext(). hasNext() only tests whether the cursor differs from the size. Take five elements and remove the one at index 3 during its iteration. The cursor is already 4, the size drops from 5 to 4, hasNext() finds 4 equal to 4 and reports the end. The loop exits normally, next() is never called again, so nothing is checked and the former last element is never visited.

This is why the documentation calls the fail-fast behaviour best-effort and says a program must not depend on the exception for correctness.

Re: ConcurrentModificationException when removing from a Java list inside a for-each loop

#4

The fixes, in order of preference. Since Java 8 there is a one-liner: sensors.removeIf(Sensor::isStale);, which on an ArrayList is a single O(n) pass.

Or use an explicit iterator and remove through it, so that it can keep its own bookkeeping straight: Iterator<Sensor> it = sensors.iterator(); while (it.hasNext()) { if (it.next().isStale()) it.remove(); }. Or build a new list, sensors = sensors.stream().filter(s -> !s.isStale()).collect(Collectors.toList());, which leaves the original list untouched and is the better choice when other code still holds a reference to it.

Re: ConcurrentModificationException when removing from a Java list inside a for-each loop

#5

An index loop works only if you compensate for the shift, and the simplest way is to go backwards: for (int i = sensors.size() - 1; i >= 0; i--) { if (sensors.get(i).isStale()) sensors.remove(i); }. Going forwards, after remove(i) the next element slides into position i and the i++ skips it, so of two adjacent stale sensors the second survives. There is no exception, just a wrong result.

Each remove(i) on an ArrayList shifts the tail, so removing many elements this way is O(n²), where removeIf is O(n). The same exception applies to maps: removing from a HashMap while looping over its keySet() or entrySet() throws, and the cure is map.entrySet().removeIf(...) or the iterator's own remove().

Re: ConcurrentModificationException when removing from a Java list inside a for-each loop

#6

With real threads, for instance a reader thread adding sensors while a UI thread iterates, the same exception can appear, but none of the fixes above is enough, because ArrayList is not thread-safe at all. Without synchronisation you can also get lost updates or an ArrayIndexOutOfBoundsException.

The options are to guard both the iteration and the modification with the same lock, or to use CopyOnWriteArrayList, whose iterators work on a snapshot and never throw. Every write copies the array, so it suits small lists that are read often and changed rarely, and its iterator does not support remove(). Collections.synchronizedList makes individual calls atomic, but iteration still needs a synchronized (list) block around the whole loop.

TEP COMMUNITY