Fundamentals 9 min read

8 Reliable Ways to Remove Elements from a Java List (And Why Some Fail)

This article examines eight common techniques for deleting elements from a Java List, explains why certain loop‑based approaches cause errors or unexpected results, and highlights the three reliable methods—including reverse for‑loops, iterator.remove, and Stream filtering—that work correctly.

macrozheng
macrozheng
macrozheng
8 Reliable Ways to Remove Elements from a Java List (And Why Some Fail)

During a recent conversation a developer struggled to delete elements from a Java

List

and encountered runtime errors. The article analyzes why this happens and presents several ways to perform the deletion correctly.

Problem

Given the following list:

<code>public List<String> initList = Arrays.asList("张三", "李四", "周一", "刘四", "李强", "李白");</code>

How can we remove all elements whose name starts with "李"?

Methods

We analyze several possible implementations.

1. Ordinary for loop deletion (unreliable)

<code>/**
 * 普通 for 循环删除
 */
@Test
public void remove1() {
    List<String> list = new ArrayList(initList);
    for (int i = 0; i < list.size(); i++) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}
</code>

Output:

[张三, 周一, 刘四, 李白]

The loop uses

list.size()

that changes during iteration, causing the loop to terminate early and leave some elements undeleted.

2. Ordinary for loop with size variable (throws exception)

<code>/**
 * 普通 for 循环删除(size提出变量)
 */
@Test
public void remove2() {
    List<String> list = new ArrayList(initList);
    int size = list.size();
    for (int i = 0; i < size; i++) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}
</code>

Result: an

IndexOutOfBoundsException

because

size

is fixed while the actual list shrinks.

3. Ordinary for loop reverse order (reliable)

<code>/**
 * 普通 for 循环倒序删除
 */
@Test
public void remove3() {
    List<String> list = new ArrayList(initList);
    for (int i = list.size() - 1; i > 0; i--) {
        String str = list.get(i);
        if (str.startsWith("李")) {
            list.remove(i);
        }
    }
    System.out.println(list);
}
</code>

Output:

[张三, 周一, 刘四]

Iterating backwards prevents index shifting issues, making this approach safe.

4. Enhanced for loop deletion (throws exception)

<code>/**
 * 增强 for 循环删除
 */
@Test
public void remove3() {
    List<String> list = new ArrayList(initList);
    for (String element : list) {
        if (element.startsWith("李")) {
            list.remove(element);
        }
    }
    System.out.println(list);
}
</code>

Result:

java.util.ConcurrentModificationException

because the underlying iterator detects concurrent modification.

5. Iterator loop with iterator.remove() (reliable)

<code>/**
 * 迭代器循环删除(iterator.remove)
 */
@Test
public void remove4() {
    List<String> list = new ArrayList(initList);
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
        String str = iterator.next();
        if (str.contains("李")) {
            iterator.remove();
        }
    }
    System.out.println(list);
}
</code>

Output:

[张三, 周一, 刘四]

The iterator’s

remove

method synchronizes the expected modification count, avoiding the concurrent‑modification error.

6. Iterator loop with list.remove (throws exception)

<code>/**
 * 迭代器循环删除(list.remove)
 */
@Test
public void remove5() {
    List<String> list = new ArrayList(initList);
    for (Iterator<String> ite = list.iterator(); ite.hasNext(); ) {
        String str = ite.next();
        if (str.contains("李")) {
            list.remove(str);
        }
    }
    System.out.println(list);
}
</code>

Result: another

ConcurrentModificationException

because removal is performed on the list rather than through the iterator.

7. Collection forEach deletion (throws exception)

<code>/**
 * list.forEach 删除
 */
@Test
public void remove6() {
    List<String> list = new ArrayList(initList);
    list.forEach((e) -> {
        if (e.contains("李")) {
            list.remove(e);
        }
    });
    System.out.println(list);
}
</code>

Result:

ConcurrentModificationException

because

forEach

internally uses an iterator.

8. Stream filter (reliable)

<code>/**
 * stream filter 过滤
 */
@Test
public void remove7() {
    List<String> list = new ArrayList(initList);
    list = list.stream().filter(e -> !e.startsWith("李")).collect(Collectors.toList());
    System.out.println(list);
}
</code>

Output:

[张三, 周一, 刘四]

This approach leverages the Stream API to create a new list without the unwanted elements, offering concise and safe removal.

Summary

The article presents eight ways to delete elements from a Java

List

. Only three methods are reliable: reverse‑order for loops, iterator‑based removal, and Stream filtering. The other approaches either miss elements or raise

ConcurrentModificationException

, illustrating common pitfalls for developers.

JavalistStreamIteratorCollectionConcurrentModificationExceptionLoop
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.