Java example

How to remove items from a list while iterating in Java

7 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

Don't call list.remove() inside a for-each loop. On Java 8+ use removeIf: list.removeIf(x -> x.isEmpty()); removes every match in one pass. When you need a loop, remove through the iterator with it.remove(), never through the list.

A for-each loop over a list is really an Iterator, and that iterator checks on every step that nobody changed the list behind its back. Calling list.remove() inside the loop does exactly that, so the next step throws ConcurrentModificationException, even in a single-threaded program. The fixes are to let the collection do the removal (removeIf), to remove through the iterator itself, to loop by index from the end, or to build a new list and leave the old one alone. This page shows each one, then the two traps waiting next to them: remove(1) on a List<Integer>, and lists that can't be changed at all. Each example runs on this page: hit Run, then edit the code and run it again.

1removeIf (Java 8+)Recommended

Collection.removeIf(predicate) removes every element the predicate returns true for and returns whether anything was removed. There is no loop to write and so no loop to get wrong. On an ArrayList it is also the fastest option: it marks the matches first and then shifts the survivors down once, where removing items one at a time shifts the tail of the array for every removal. It works on any collection, including the values(), keySet() and entrySet() views of a map, and removing from a view removes the entry from the map.

Main.java

Output

Prints [Ann, Bob, Cy, Bea], true [Ann, Cy] and false, then the map without its zero counts, {pens=12, desks=3}, and finally {pens=12}. Use values().removeIf when the rule only looks at the value, keySet().removeIf when it only looks at the key, and entrySet().removeIf when it needs both. The list was built with new ArrayList<>(List.of(...)) on purpose: removeIf on List.of(...) itself throws, as section 7 shows. For looping over a map without removing anything, see iterate over a map.

2Why list.remove() inside a for-each throws

The compiler turns for (String name : names) into an Iterator loop. An ArrayList counts its structural changes, and the iterator remembers that count when it starts. names.remove(name) bumps the count without telling the iterator, so its next call to next() sees the mismatch and throws ConcurrentModificationException. The name is misleading: no second thread is involved. The check is also a best-effort safety net rather than a guarantee, and the second half of this example shows it staying silent.

Main.java

Output

Prints ConcurrentModificationException, then [Ann, Cy, Dee]: the removal itself succeeded, and the exception came from the loop's next step, so catching it leaves you with a half-processed list. The second loop prints [java, tmp] with no exception at all. Removing the second-to-last element shrinks the list so that the iterator's position equals the new size, hasNext() returns false, and the loop ends before the last tmp is ever checked. The one safe pattern is removing a single element and then leaving the loop with break straight away; for anything else, use one of the approaches below.

3Iterator.remove() (any Java version)

When the loop body does more than decide yes or no, such as moving the removed items somewhere else, logging them or stopping at the first match, write the iterator out yourself and remove through it. it.remove() deletes the element the last next() returned and keeps the iterator's bookkeeping in step with the list, so there is nothing to detect. This was the standard answer before Java 8, and it is what removeIf does for you on collections without a faster implementation of their own.

Main.java

Output

Prints [write, deploy, review] and removed 2: [test, test]. The second part prints IllegalStateException, because remove() is only allowed once per next(), and then [deploy, review]. Maps work the same way through map.entrySet().iterator(). You will also see this loop written as for (Iterator<String> it = list.iterator(); it.hasNext(); ), which keeps it scoped to the loop. A ListIterator adds set() and add() for replacing or inserting during the same pass.

4A backwards index loop, and remove(int) vs remove(Object)

A plain for (int i = 0; ...) loop never throws, but walking forward with list.remove(i) skips elements: each removal shifts the rest of the list one place left, so the next element lands on index i and the i++ steps over it. Walking from the end fixes that, because a removal only moves elements you have already checked. Index loops on a List<Integer> also hit a second trap: List has both remove(int index) and remove(Object o), and an int argument always picks the index version.

Main.java

Output

The forward loop prints [1, 2, 3, 4]: one 2 survived because it slid into the slot the loop had just checked. The backwards loop prints [1, 3, 4]. Then ids.remove(1) removes the element at index 1 and returns it, printing 20 [10, 30, 1], while ids.remove(Integer.valueOf(1)) removes the first element equal to 1 and returns a boolean, printing true [10, 30]. A cast such as remove((Integer) 1) works too. The backwards loop is useful when the rule depends on the index, but each remove(i) on an ArrayList shifts the whole tail, so for many removals removeIf is faster.

5A new list with a stream filter

Sometimes the right move is not to remove anything. stream().filter(...) builds a new list from the elements you want to keep and leaves the source untouched. That matters when other code still holds the original list, and it is the only one of these approaches that works on a list you can't modify, such as one from List.of. Note the direction of the test: filter takes a keep condition, the opposite of removeIf.

Main.java

Output

Prints [91, 78, 88], the unchanged source [91, 45, 78, 30, 88], then [91, 78, 88, 100] and UnsupportedOperationException. Stream.toList() (Java 16+) returns an unmodifiable list, which is why the last add fails. On Java 8 use .collect(Collectors.toList()), or Collectors.toCollection(ArrayList::new) as above when you need a list you can change. To replace the old list, assign the result back to the same variable.

6Which should you use?

MethodChanges the listJava versionBest for
list.removeIf(x -> ...)Yes, in place8+Most code: one line, one pass
it.remove()Yes, in placeAnyLoops that also use the removed item or stop early
for (int i = size - 1; i >= 0; i--)Yes, in placeAnyRules that depend on the index
stream().filter(x -> ...).toList()No, a new list16+ (collect: 8+)Keeping the original, or unmodifiable sources
map.entrySet().removeIf(e -> ...)Yes, in place8+Maps, by key and value
for (x : list) list.remove(x)Throws or skipsAnyNever

7UnsupportedOperationException: lists you can't change

If remove or removeIf throws UnsupportedOperationException, the loop is not the problem: the list itself doesn't allow removals. Arrays.asList returns a fixed-size view over an array, and List.of, List.copyOf, Stream.toList() and Collections.unmodifiableList return lists that can't be changed at all. The fix is the same for each: copy the elements into an ArrayList and remove from the copy, or filter into a new list as in section 5.

Main.java

Output

Prints Arrays.asList: UnsupportedOperationException, List.of: UnsupportedOperationException, then [a, c] [a, b, c]. The two kinds differ in detail. Arrays.asList still allows set(), and its removeIf only throws when something matches (with no match it returns false). List.of lists reject every change, and their removeIf throws even when nothing matches. More on both in convert an array to a list and initialize a list in one line.

Frequently asked questions

How do I remove elements from a list while iterating over it in Java?

On Java 8+, skip the loop and call list.removeIf(x -> condition), which removes every match in one pass. If you need a loop, use an explicit Iterator and call it.remove() after it.next(). Looping backwards by index with list.remove(i) also works. Never call list.remove(...) inside a for-each loop over the same list.

Why do I get a ConcurrentModificationException when removing in a for-each loop?

A for-each loop uses the list's iterator, and ArrayList iterators check on each next() that the list was not structurally changed by anything other than the iterator itself. list.remove(x) changes it, so the following step throws. It is not about threads. Remove through it.remove() or use removeIf instead. Removing one element and immediately calling break is safe, because the iterator is never used again.

Why does list.remove(1) remove the wrong element from a List<Integer>?

List has two overloads, remove(int index) and remove(Object o), and an int argument picks the index version without autoboxing. So remove(1) removes the second element, and on a short list a value like remove(30) throws IndexOutOfBoundsException: Index 30 out of bounds for length 3. To remove by value, pass an Integer: list.remove(Integer.valueOf(30)) or list.remove((Integer) 30).

Why do I get an UnsupportedOperationException when removing from a List?

The list does not allow removals. Arrays.asList(...) is a fixed-size view over an array, and List.of(...), List.copyOf(...), stream.toList() and Collections.unmodifiableList(...) are unmodifiable. Copy it first with new ArrayList<>(list) and remove from the copy, or build a filtered list with a stream.

How do I remove entries from a HashMap while iterating?

Use map.entrySet().removeIf(e -> ...), or map.values().removeIf(...) / map.keySet().removeIf(...) when the rule only needs the value or the key. In a loop, iterate map.entrySet().iterator() and call it.remove(). Calling map.remove(key) inside a for-each over map.keySet() has the same problem as the list version: it usually throws ConcurrentModificationException, and when it does not, the loop may have stopped early.

Does CopyOnWriteArrayList fix ConcurrentModificationException?

It avoids it: a CopyOnWriteArrayList iterator walks a snapshot, so list.remove(x) inside a for-each works. But every write copies the whole underlying array, and its iterator does not support it.remove(), which throws UnsupportedOperationException. It is meant for lists that many threads read and rarely change, not as a fix for a single-threaded loop. Use removeIf for that.

Run it yourself

Open any of these in the full Java editor: tweak, run, and share.

Java playground