Java
foreach loop
remove method
programming
debugging

Calling remove in foreach loop in Java

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Removing elements from a Java collection while iterating over it is a common source of bugs. The short version is that you should not call the collection's own remove method from inside an enhanced for loop, because that loop is backed by an iterator and structural changes made behind the iterator's back trigger fail-fast behavior.

Why foreach Removal Fails

The enhanced for loop is syntax sugar for iterator-based traversal. When you write this:

java
1for (String name : names) {
2    if (name.startsWith("B")) {
3        names.remove(name);
4    }
5}

Java is effectively using an iterator under the hood. That iterator expects the collection structure to remain unchanged except through the iterator itself. If the collection is modified directly, the iterator notices that its internal modification count no longer matches the collection, and a ConcurrentModificationException is usually thrown.

A minimal example is:

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Main {
5    public static void main(String[] args) {
6        List<String> names = new ArrayList<>();
7        names.add("Alice");
8        names.add("Bob");
9        names.add("Carol");
10
11        for (String name : names) {
12            if (name.startsWith("B")) {
13                names.remove(name);
14            }
15        }
16    }
17}

This pattern is unsafe even if it appears to work occasionally with certain inputs.

Use the Iterator's remove Method Instead

If you need to remove elements during iteration, iterate explicitly with an Iterator and call iterator.remove().

java
1import java.util.ArrayList;
2import java.util.Iterator;
3import java.util.List;
4
5public class Main {
6    public static void main(String[] args) {
7        List<String> names = new ArrayList<>();
8        names.add("Alice");
9        names.add("Bob");
10        names.add("Carol");
11
12        Iterator<String> iterator = names.iterator();
13        while (iterator.hasNext()) {
14            String name = iterator.next();
15            if (name.startsWith("B")) {
16                iterator.remove();
17            }
18        }
19
20        System.out.println(names);
21    }
22}

This works because the iterator updates its own internal state when removal happens through its API.

Use removeIf When You Only Need Filtering

If your goal is simply "remove every element matching this condition," Java 8 added a cleaner option: removeIf.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Main {
5    public static void main(String[] args) {
6        List<String> names = new ArrayList<>();
7        names.add("Alice");
8        names.add("Bob");
9        names.add("Carol");
10
11        names.removeIf(name -> name.startsWith("B"));
12        System.out.println(names);
13    }
14}

This is usually the most readable approach because the collection handles the iteration internally.

When a Two-Phase Approach Is Better

Sometimes removal is only one part of a larger loop body, and collecting items first is easier to reason about. In that case, gather the elements to remove and perform the structural update after iteration finishes.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class Main {
5    public static void main(String[] args) {
6        List<String> names = new ArrayList<>();
7        names.add("Alice");
8        names.add("Bob");
9        names.add("Bill");
10        names.add("Carol");
11
12        List<String> toRemove = new ArrayList<>();
13        for (String name : names) {
14            if (name.startsWith("B")) {
15                toRemove.add(name);
16            }
17        }
18
19        names.removeAll(toRemove);
20        System.out.println(names);
21    }
22}

This is slightly more verbose, but it keeps the iteration logic separate from the structural change, which can make complex code easier to maintain.

Common Pitfalls

The biggest mistake is assuming foreach is independent from iterators. It is not. The iterator is still there, just hidden by syntax sugar.

Another mistake is switching to an index-based loop without thinking about shifting indices. That can work for lists, but careless index removal can skip elements or throw bounds errors.

Developers also sometimes hear the term ConcurrentModificationException and assume multiple threads are involved. In many cases, there is no thread concurrency at all. The name refers to modifying the collection while it is being iterated, not necessarily to multithreading.

Finally, iterator.remove() only removes the current element after a successful next() call. Calling it at the wrong time causes its own exception.

Summary

  • Do not call the collection's remove method inside an enhanced for loop.
  • Enhanced for loops are backed by iterators and usually fail fast on structural modification.
  • Use Iterator.remove() when you must remove while iterating.
  • Use removeIf for simple predicate-based filtering.
  • For more complex cases, collect items first and remove them afterward.

Course illustration
Course illustration

All Rights Reserved.