Question

How to Remove Items While Iterating a Collection in Java Without ConcurrentModificationException

javacollectionsiteration

Question

I want to iterate through a Java Collection and remove elements that match a condition. A loop like this is known to cause ConcurrentModificationException:

for (Object item : l) {
    if (condition(item)) {
        l.remove(item);
    }
}

For example:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

This throws:

Exception in thread "main" java.util.ConcurrentModificationException

Why does this happen even though only one thread is involved? What is the best way to remove items from a Collection while looping through it, especially when the collection is arbitrary and not necessarily an ArrayList?

Short Answer

By the end of this page, you will understand why ConcurrentModificationException happens in Java when modifying a collection during iteration, why it is not really about multiple threads, and how to safely remove elements using an Iterator, removeIf, or other appropriate patterns.

Concept

ConcurrentModificationException happens when a collection is structurally modified while it is being iterated in an unsafe way.

In Java, the enhanced for loop:

for (int i : l) {
    ...
}

is actually using an Iterator behind the scenes. That iterator expects the collection to remain unchanged except through the iterator's own remove() method.

When you call:

l.remove(i);

you modify the collection directly, not through the iterator. The iterator notices that the collection changed unexpectedly and throws ConcurrentModificationException.

Why the name is confusing

Despite its name, this exception does not require multiple threads. Here, "concurrent modification" means:

  • one part of your code is iterating
  • another operation modifies the collection at the same time

That can happen in a single thread.

Why this matters

If Java allowed arbitrary changes during iteration, the iterator could become inconsistent:

  • it might skip elements
  • process the same element twice
  • read invalid internal state

So Java collections often use fail-fast iterators. They detect unsafe modification early and throw an exception instead of continuing with unpredictable behavior.

The safe idea

If you want to remove elements while iterating, use tools designed for that:

  • Iterator.remove() for general collections
  • removeIf(...) in modern Java
  • collect items first, then remove them later

The correct choice depends on your Java version and coding style.

Mental Model

Imagine a collection as a list of names on paper, and an iterator as your finger moving down the page.

If you suddenly tear out a line from the paper while your finger is still tracking positions, your finger may now point to the wrong place. You might skip a name or lose track completely.

Java's iterator says: "If you want to remove the current line, ask me to do it, so I can keep my position correct."

That is exactly what Iterator.remove() does.

Take Quiz

Syntax and Examples

Safe removal with Iterator

This is the classic solution that works for an arbitrary Collection:

Collection<Integer> l = new ArrayList<>();

for (int i = 0; i < 10; ++i) {
    l.add(4);
    l.add(5);
    l.add(6);
}

Iterator<Integer> it = l.iterator();
while (it.hasNext()) {
    int value = it.next();
    if (value == 5) {
        it.remove();
    }
}

System.out.println(l);

Why this works

  • it.next() moves to the next element
  • it.remove() removes the element most recently returned by next()
  • the iterator updates its own internal state safely

Modern Java: removeIf

If you are using Java 8 or later, this is often the cleanest option:

Collection<Integer> l = new ArrayList<>();

for (int i = 0; i < 10; ++i) {
    l.add(4);
    l.add(5);
    l.add(6);
}

l.removeIf(value -> value == 5);

System.out.println(l);

This removes every element matching the condition.

What not to do

for (int value : l) {
    if (value == 5) {
        l.remove(value); // unsafe during enhanced for-loop
    }
}

This is unsafe because the enhanced for loop is using an iterator internally, while l.remove(...) modifies the collection externally.

Step by Step Execution

Consider this safe example:

Collection<Integer> l = new ArrayList<>();
l.add(4);
l.add(5);
l.add(6);

Iterator<Integer> it = l.iterator();
while (it.hasNext()) {
    int value = it.next();
    if (value == 5) {
        it.remove();
    }
}

Step by step

Initial collection:

[4, 5, 6]

1. Create the iterator

Iterator<Integer> it = l.iterator();

The iterator is now ready to walk through the collection.

2. First loop iteration

int value = it.next();
  • value becomes 4
  • condition value == 5 is false
  • nothing is removed

Collection remains:

[4, 5, 6]

3. Second loop iteration

int value = it.next();
  • value becomes 5
  • condition is true
it.remove();

Now the iterator removes the current element safely.

Collection becomes:

[4, 6]

4. Third loop iteration

int value = it.next();
  • value becomes 6
  • condition is false

5. End of loop

it.hasNext() is now false, so iteration ends.

Final collection:

[4, 6]

Key rule

During iteration, the iterator must stay in control of removals if you want safe structural changes.

Real World Use Cases

Filtering invalid data

You may want to remove bad values from a collection:

users.removeIf(user -> user.getEmail() == null);

Cleaning cached items

Applications often remove expired entries:

sessions.removeIf(session -> session.isExpired());

Processing API results

After fetching data, you might discard incomplete records:

orders.removeIf(order -> order.getItems().isEmpty());

Validation pipelines

When preparing data for saving, developers remove unusable values:

numbers.removeIf(n -> n < 0);

General collection handling

Because Iterator works with any Collection, it is useful when you do not know the exact implementation type, such as:

  • ArrayList
  • LinkedList
  • HashSet
  • other custom collection implementations
Take Quiz

Real Codebase Usage

In real projects, developers usually do not write unsafe removal loops. They use one of a few standard patterns.

1. Iterator-based removal

Common when working with a generic Collection or when compatibility matters:

Iterator<Task> it = tasks.iterator();
while (it.hasNext()) {
    Task task = it.next();
    if (task.isCompleted()) {
        it.remove();
    }
}

2. removeIf for simple filtering

This is common in modern Java code because it is short and readable:

tasks.removeIf(Task::isCompleted);

3. Build a new filtered collection

Sometimes developers prefer not to mutate the original collection:

List<Task> activeTasks = tasks.stream()
    .filter(task -> !task.isCompleted())
    .toList();

This is useful when:

  • immutability is preferred
  • the original collection should remain unchanged
  • the code is easier to reason about by creating a result instead of modifying in place

4. Guarding mutation points

In larger codebases, collection updates are often centralized in service methods so mutation is controlled and easier to test.

5. Validation and cleanup passes

A common pattern is to iterate once and remove invalid entries using a clear rule, often before persistence, API output, or batch processing.

Common Mistakes

Mistake 1: Removing from the collection directly inside a for-each loop

Broken code:

for (Integer value : l) {
    if (value == 5) {
        l.remove(value);
    }
}

Why it fails

The loop uses an iterator internally, but the removal happens directly on the collection.

Fix

Use Iterator.remove() or removeIf(...).

Mistake 2: Calling iterator.remove() before next()

Broken code:

Iterator<Integer> it = l.iterator();
it.remove();

Why it fails

remove() can only remove the item most recently returned by next().

Fix

Call next() first:

Iterator<Integer> it = l.iterator();
while (it.hasNext()) {
    Integer value = it.next();
    if (value == 5) {
        it.remove();
    }
}

Mistake 3: Calling remove() twice for one next()

Broken code:

Iterator<Integer> it = l.iterator();
while (it.hasNext()) {
    Integer value = it.next();
    if (value == 5) {
        it.remove();
        it.remove();
    }
}

Why it fails

Each call to remove() must match one preceding next() call.

Mistake 4: Confusing element removal with index removal

With List<Integer>, this can be tricky:

List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);

list.remove(1);

This removes the element at index 1, not the value 1.

To remove the value 1, use:

list.remove(Integer.valueOf(1));

This is less relevant for a general Collection, but very important for List<Integer>.

Mistake 5: Assuming the exception is about threads only

It can happen in a single thread whenever iteration and structural modification happen in an unsafe combination.

Comparisons

ApproachWorks on arbitrary CollectionRemoves during iteration safelyReadabilityNotes
Enhanced for + collection.remove(...)YesNoHighUsually throws ConcurrentModificationException
Iterator + iterator.remove()YesYesMediumClassic and reliable solution
removeIf(...)YesYesHighBest for simple predicate-based removal in Java 8+
Build a new filtered collectionUsuallyYesHighGood when you do not want to mutate the original

Iterator.remove() vs removeIf()

FeatureIterator.remove()removeIf()
Java versionOlder Java versionsJava 8+
Good for complex loop logicYesSometimes
Very conciseNoYes
Explicit control over iterationYesNo

When to choose which

  • Use Iterator.remove() when you need full control while scanning elements.
  • Use removeIf() when the rule is just "remove elements matching this condition".
  • Use a new filtered collection when mutation should be avoided.
Take Quiz

Cheat Sheet

Safe ways to remove while iterating in Java

Use an iterator

Iterator<Type> it = collection.iterator();
while (it.hasNext()) {
    Type item = it.next();
    if (condition(item)) {
        it.remove();
    }
}

Use removeIf in Java 8+

collection.removeIf(item -> condition(item));

Avoid this

for (Type item : collection) {
    if (condition(item)) {
        collection.remove(item); // unsafe
    }
}

Rules

  • Enhanced for uses an iterator internally.
  • Do not structurally modify a collection during iteration unless using the iterator's own remove().
  • ConcurrentModificationException can happen in a single thread.
  • Iterator.remove() removes the last item returned by next().
  • Call remove() at most once per next().

Good default choices

  • Simple condition: removeIf(...)
  • General safe loop: Iterator.remove()
  • Preserve original data: create a new filtered collection

FAQ

Why does ConcurrentModificationException happen in one thread?

Because the problem is unsafe modification during iteration, not necessarily multiple threads. One part of the code is iterating while another changes the collection structure.

Is it ever safe to call collection.remove() inside a for-each loop?

Generally no. The safe approach is to use Iterator.remove() or removeIf(...).

What is the best way to remove items from a generic Collection?

Use an Iterator and call iterator.remove() while iterating, or use removeIf(...) in Java 8 and later.

Does removeIf() work for all collections?

It works for many standard Java collections, but the collection must support removal operations. Unsupported collections may throw UnsupportedOperationException.

Why does Java use fail-fast iterators?

To detect unsafe changes early and prevent unpredictable behavior like skipped elements or corrupted traversal.

Can I use a normal index loop instead?

Only if you specifically have a List and index-based access makes sense. For a general Collection, you should not rely on indexing.

How do I remove only the current element during iteration?

Use Iterator.remove() immediately after next() returns the element you want to remove.

Take Quiz

Mini Project

Description

Create a small Java program that cleans a collection of integers by removing unwanted values. This demonstrates safe in-place removal using both Iterator and removeIf, which is a common task in data cleanup and validation code.

Goal

Build a program that removes all negative numbers and prints the cleaned collection safely.

Requirements

  • Create a Collection<Integer> with a mix of positive, negative, and zero values.
  • Remove all negative values safely while iterating.
  • Print the collection before and after cleanup.
  • Implement at least one solution using Iterator.
  • Optionally show a second version using removeIf.
Take Quiz

Keep learning

Related questions

View more