You need to remove elements from a List while iterating it. What are the correct ways, and what are the pitfalls of each?
answer
- removeIf for conditional delete
- Iterator.remove for complex logic
- iterate a copy, remove from original
- index loop: shift-left skip bug → loop backwards
- Arrays.asList/List.of throw UnsupportedOperationException
basics
~20 sDon't call list.remove() inside a for-each loop — it throws an error. Instead use removeIf() with a condition, or use an explicit Iterator and call iterator.remove(). You can also loop over a copy of the list and remove from the original.
solid answer
~40 sThe safe options: (1) removeIf(predicate) — cleanest for conditional removal, Java 8+. (2) An explicit Iterator with iterator.remove() — needed when removal logic is complex or you must do other work. (3) Iterate a copy (new ArrayList<>(list)) and remove from the original. (4) Collect the targets in a separate list, then removeAll. The classic pitfall is calling list.remove() inside a for-each, which throws ConcurrentModificationException because the hidden iterator's modCount check fails. A subtler pitfall is index-based loops: when you remove at index i and keep incrementing i, you skip the next element (the list shifts left); the fix is to not increment after a removal or iterate backwards. Also note removeIf on some collections isn't atomic/thread-safe, and on fixed-size lists like Arrays.asList it throws UnsupportedOperationException.
code
java · 15 lines// Index-loop shift bug and its fix:
List<Integer> nums = new ArrayList<>(List.of(1, 2, 2, 3));
// BUG: removing forward with i++ skips an element
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) == 2) nums.remove(i); // skips the second 2
}
// FIX A: iterate backwards
for (int i = nums.size() - 1; i >= 0; i--) {
if (nums.get(i) == 2) nums.remove(i);
}
// FIX B (preferred): removeIf
nums.removeIf(n -> n == 2);go deeper
Knows to use removeIf or Iterator.remove instead of removing inside a for-each, and that the naive way throws CME.
Can list all four safe techniques and explain the index-loop shift-left skip bug plus its backward-iteration fix.
Weighs the techniques on cost (O(n²) copy/removeAll), correctness (equals semantics of removeAll), and constraints (immutable/fixed-size lists, thread-safety).
Sets idioms for the codebase (default to removeIf), flags performance traps in hot paths, and chooses concurrent structures when removal happens under concurrency.
## The task You're iterating a `List` and want to delete some elements as you go. Doing this naively throws `ConcurrentModificationException` (CME). Here are the correct techniques and the trap in each. ## Why the naive way fails ```java for (String s : list) { // for-each = a hidden Iterator if (cond(s)) list.remove(s); // structural change via the LIST, not the iterator } ``` The for-each loop's hidden iterator snapshots `modCount` into `expectedModCount`. `list.remove` bumps `modCount`. The next `next()` sees them differ and throws CME. (See the modCount question for the mechanism.) ## Correct technique 1: `removeIf` (preferred for conditional delete) ```java list.removeIf(s -> cond(s)); ``` Clean, Java 8+, defined on `Collection`. Internally it uses the iterator's `remove`, so it's CME-safe. **Pitfalls:** the predicate must be side-effect-free; it isn't thread-safe across threads; and it throws `UnsupportedOperationException` on fixed-size/immutable lists (`Arrays.asList(...)`, `List.of(...)`). ## Correct technique 2: explicit `Iterator.remove()` ```java Iterator<String> it = list.iterator(); while (it.hasNext()) { String s = it.next(); if (cond(s)) it.remove(); // safe: re-syncs expectedModCount } ``` Use this when removal depends on stateful logic, or you must do more than a boolean test. **Pitfall:** you must call `it.next()` before `it.remove()`, and `remove()` once per `next()`; otherwise you get `IllegalStateException`. ## Correct technique 3: iterate a copy, modify the original ```java for (String s : new ArrayList<>(list)) { if (cond(s)) list.remove(s); } ``` The iterator walks the copy; the structural changes hit the original, so no CME. **Pitfall:** O(n) extra memory for the copy, and `list.remove(Object)` is O(n) per call on an ArrayList (so this can be O(n²)). ## Correct technique 4: collect-then-remove ```java List<String> toRemove = new ArrayList<>(); for (String s : list) if (cond(s)) toRemove.add(s); list.removeAll(toRemove); ``` Reads cleanly; one bulk removal. **Pitfall:** `removeAll` uses `equals`, so it removes *all* equal elements, and is O(n·m) unless `toRemove` is a Set. ## The index-loop trap (a different bug, not CME) ```java for (int i = 0; i < list.size(); i++) { if (cond(list.get(i))) list.remove(i); // no CME, but... } ``` Index loops use `get`/`remove(index)`, **not** an iterator, so there is **no CME**. But when you remove at `i`, every later element shifts left by one; then `i++` skips the element that moved into slot `i`. **Fixes:** don't increment after a removal (`else i++;`), or iterate **backwards** (`for (int i = size-1; i >= 0; i--)`), which is immune to the shift. ## Choosing - Simple predicate, single-threaded → `removeIf`. - Complex/stateful removal → explicit `Iterator.remove`. - Need the elements elsewhere too → collect-then-`removeAll` (with a Set for the targets). - Shared across threads → don't do any of these on a plain list; use a concurrent collection (e.g. `CopyOnWriteArrayList`, where you mutate the list, not the iterator).
- Why does removeIf throw UnsupportedOperationException on List.of(...)?List.of and Arrays.asList return fixed-size/immutable lists. removeIf needs to remove elements (change size), which those lists don't support, so it throws. Wrap in a new ArrayList<>(...) first if you must mutate.
- Does an index-based for loop (for i = 0; i < size; i++) throw ConcurrentModificationException when you remove?No. It uses get(i)/remove(i) directly, not an iterator, so there's no modCount check. But it has the shift-left bug that silently skips elements — iterate backwards or use removeIf to avoid it.
saying these in an interview costs you the question
- Removing via list.remove() inside a for-each and expecting it to work.
- Using a forward index loop with i++ after removal, silently skipping elements.
- Calling removeIf on List.of/Arrays.asList and not anticipating UnsupportedOperationException.
- Calling iterator.remove() twice per next(), or before any next(), causing IllegalStateException.