Question
How to Remove Items While Iterating a Collection in Java Without ConcurrentModificationException
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 collectionsremoveIf(...)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.
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 elementit.remove()removes the element most recently returned bynext()- 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();
valuebecomes4- condition
value == 5is false - nothing is removed
Collection remains:
[4, 5, 6]
3. Second loop iteration
int value = it.next();
valuebecomes5- 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();
valuebecomes6- 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:
ArrayListLinkedListHashSet- other custom collection implementations
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
| Approach | Works on arbitrary Collection | Removes during iteration safely | Readability | Notes |
|---|---|---|---|---|
Enhanced for + collection.remove(...) | Yes | No | High | Usually throws ConcurrentModificationException |
Iterator + iterator.remove() | Yes | Yes | Medium | Classic and reliable solution |
removeIf(...) | Yes | Yes | High | Best for simple predicate-based removal in Java 8+ |
| Build a new filtered collection | Usually | Yes | High | Good when you do not want to mutate the original |
Iterator.remove() vs removeIf()
| Feature | Iterator.remove() | removeIf() |
|---|---|---|
| Java version | Older Java versions | Java 8+ |
| Good for complex loop logic | Yes | Sometimes |
| Very concise | No | Yes |
| Explicit control over iteration | Yes | No |
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.
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
foruses an iterator internally. - Do not structurally modify a collection during iteration unless using the iterator's own
remove(). ConcurrentModificationExceptioncan happen in a single thread.Iterator.remove()removes the last item returned bynext().- Call
remove()at most once pernext().
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.
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.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.