【Java踩坑】List遍历中remove元素报错?ConcurrentModificationException终极解决方法

【Java踩坑】List遍历中remove元素报错?ConcurrentModificationException终极解决方法

日常开发中,我们经常需要遍历List集合并删除指定元素。很多新手(包括曾经的我)第一反应就是用普通for循环/增强for循环遍历,直接调用 list.remove(),结果直接抛出异常:ConcurrentModificationException。

今天就详细复盘这个高频Bug,分析报错根源,分享几种靠谱的解决方案,以及各自的适用场景,彻底搞定List遍历删除元素的问题。

QQ20260825-033530.png

一、问题复现:错误代码(必报错)
先看最常见的两种错误写法,运行直接抛出并发修改异常。

  1. 增强for循环遍历删除(完全不可用)
    import java.util.ArrayList;
    import java.util.List;

public class ListRemoveDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
list.add("C++");

    // 增强for循环遍历删除元素
    for (String language : list) {
        if ("Go".equals(language)) {
            list.remove(language);
        }
    }
    System.out.println(list);
}

}

  1. 普通for循环正向遍历删除(隐藏Bug)
    // 普通for正向遍历删除
    for (int i = 0; i < list.size(); i++) {
    String language = list.get(i);
    if ("Go".equals(language)) {
    list.remove(i);
    }
    }
    System.out.println(list);
    报错信息
    Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)

二、报错核心原因分析

很多人只知道这么写会报错,但不知道底层原理,这里简单讲清楚:

  1. 增强for循环底层依赖迭代器(Iterator),迭代器遍历过程中,会维护一个 modCount(修改次数)和 expectedModCount(预期修改次数)。

  2. 当我们调用 list.remove() 时,只会让 modCount++,不会更新迭代器的expectedModCount。

  3. 迭代器下次执行next() 方法时,会校验两者是否相等,不相等则直接抛出 ConcurrentModificationException 并发修改异常。

而普通正向for循环的问题:删除元素后,集合长度变短,后续元素前移,会跳过一个元素,导致删除不彻底,属于隐性逻辑Bug,不会报错但结果错误。

三、四种正确解决方案(亲测可用)

方案1:迭代器Iterator遍历删除(最经典、通用)
使用迭代器自身的 remove() 方法,会同步更新 expectedModCount,避免校验失败,完美解决异常。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListRemoveDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
list.add("C++");

    // 迭代器遍历删除
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String language = iterator.next();
        if ("Go".equals(language)) {
            // 必须用迭代器的remove,不能用list的remove
            iterator.remove();
        }
    }
    // 输出结果:[Java, Python, C++]
    System.out.println(list);
}

}
优点:兼容所有List集合,性能稳定,JDK所有版本通用,无副作用。

方案2:普通for循环倒序遍历(简单快捷)
正向遍历会跳过元素,倒序遍历不会出现元素跳过问题,适合简单场景快速删除。
// 倒序遍历删除
for (int i = list.size() - 1; i >= 0; i--) {
String language = list.get(i);
if ("Go".equals(language)) {
list.remove(i);
}
}
System.out.println(list);
优点:代码简单,无需依赖迭代器;缺点:只适合根据下标删除,无法适配复杂条件。

方案3:JDK8+ removeIf(最简代码、推荐)
JDK8新增 removeIf() 方法,基于Lambda表达式,一行代码搞定条件删除,底层封装了迭代器逻辑,简洁且高效,日常开发首选。
// 一行代码删除满足条件的元素
list.removeIf(language -> "Go".equals(language));
System.out.println(list);
优点:代码极简、可读性强、底层安全无异常;缺点:仅支持JDK8及以上版本。
方案4:新建集合留存元素(规避修改原集合)
遍历原集合,将需要保留的元素存入新集合,间接实现删除效果,适合大批量数据处理场景。
List newList = new ArrayList<>();
for (String language : list) {
// 留存不满足删除条件的元素
if (!"Go".equals(language)) {
newList.add(language);
}
}
System.out.println(newList);
优点:绝对安全,无任何并发修改问题;缺点:会创建新集合,占用少量内存。

四、各方案适用场景总结

QQ20260825-033336.png

五、最终总结(避坑重点)

  • ❌ 禁止:增强for循环中直接调用 list.remove(),必报并发修改异常;
  • ❌ 禁止:正向for循环遍历删除,会出现元素漏删逻辑Bug;
  • ✅ 首选:JDK8+ 直接使用 removeIf,简洁高效;
  • ✅ 兼容低版本:使用迭代器 iterator.remove() 安全删除。

以上就是Java List遍历删除元素的全部踩坑记录和解决方案,都是日常开发高频问题,建议收藏避坑!如果对你有帮助,欢迎点赞、收藏、评论交流~

标签: none

添加新评论

  • 上一篇:
  • 下一篇: