Why ConcurrentModificationException happens with one thread

Read the explanation
What if

A for-each loop removes bob from the list it is walking. The remove works; the loop's next step throws.

Predict first

One thread walks [ann, bob, cid, dan] with for-each and removes bob when it gets there. What happens?

012345visit annvisit bobmain: names.remove("bob")removemain: next step: ConcurrentModificationExceptionthrowsmain: println: never runsnever

Tap any block to see what that thread is doing.

  • Running
  • Error
  • Skipped
Step 1/4

for-each walks the list with a hidden Iterator. One thread, no other threads anywhere.

1List<String> names = new ArrayList<>(List.of("ann", "bob", "cid", "dan"));2for (String name : names) { // a hidden Iterator checks for changes at every step3    if (name.equals("bob")) {4        names.remove(name); // changes the list behind the iterator's back5    }6}7System.out.println(names);

Explanation

What just happened

A for-each loop uses a hidden Iterator. At bob, the loop called names.remove, and the remove worked: the list became [ann, cid, dan]. On the next step, the iterator checked whether the list had changed since the loop began. It had, so it threw ConcurrentModificationException instead of guessing where to continue. No other thread was involved.

Try the what-if switch

Why it fails fast

Once the list has shifted, the iterator can’t know which element comes next. Throwing right away is safer than silently skipping or repeating elements.

Watch out

The check is “best effort”: removing the second-to-last element skips the last one without any exception.

Common questions

What causes ConcurrentModificationException?

Changing a list while a for-each loop is walking it, usually with list.remove() inside the loop. The loop's hidden iterator notices on its next step and throws. One thread is enough.

How do I remove items while looping?

Use list.removeIf(...), or loop with an explicit Iterator and call iterator.remove(). Both remove without confusing the loop.

Why didn't my loop throw when I removed an element?

If you remove the second-to-last element, the loop thinks it has reached the end and stops without throwing. The last element is never visited, which is a quieter bug.

Verified: this timeline matches a real program, checked in CI on JDK 21 and 25. How we verify. Unclear or wrong? Tell us