foreach中使用remove踩坑

问题本质:并发修改异常(ConcurrentModificationException)

foreach 循环的底层是通过 迭代器(Iterator) 实现的,迭代器在遍历集合时会维护一个"修改次数"的标记(modCount)。当你直接调用集合的 remove 方法时,会修改这个标记,但迭代器本身并不知道集合已经被修改,下一次迭代时发现标记不一致,就会抛出 ConcurrentModificationException(并发修改异常)。

简单来说:迭代器在"盯着"集合,不允许你在它遍历的时候偷偷修改集合的结构(增/删元素)。

直观示例:错误用法(会报错)

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ForeachRemoveError {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 错误:foreach循环中直接remove,会抛出ConcurrentModificationException
        for (String s : list) {
            if ("B".equals(s)) {
                list.remove(s); // 直接调用集合的remove方法
            }
        }
    }
}

运行这段代码会直接抛出异常,核心原因就是迭代器检测到集合被"非法修改"了。

正确的解决方案

方案1:使用迭代器的 remove 方法(推荐)

迭代器自身提供的 remove 方法会同步更新"修改次数"标记,避免异常:

java 复制代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorRemove {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 正确:使用迭代器的remove方法
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            if ("B".equals(s)) {
                iterator.remove(); // 迭代器自身的remove方法,安全
            }
        }

        System.out.println(list); // 输出:[A, C]
    }
}
方案2:使用普通 for 循环(倒序遍历)

如果用普通 for 循环,建议倒序遍历(避免正序删除后元素下标错乱):

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ForLoopRemove {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 正确:倒序for循环删除
        for (int i = list.size() - 1; i >= 0; i--) {
            if ("B".equals(list.get(i))) {
                list.remove(i);
            }
        }

        System.out.println(list); // 输出:[A, C]
    }
}
方案3:Java 8+ 流式处理(简洁)

如果是过滤元素,用 Stream 的 filter 更优雅:

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamRemove {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 正确:Stream过滤(生成新集合,原集合不变)
        List<String> newList = list.stream()
                .filter(s -> !"B".equals(s)) // 过滤掉"B"
                .collect(Collectors.toList());

        System.out.println(newList); // 输出:[A, C]
    }
}

总结

  1. foreach 循环底层依赖迭代器,直接调用集合 remove 会导致迭代器检测到"并发修改",抛出 ConcurrentModificationException;
  2. 安全删除集合元素的核心方式:用迭代器的 remove 方法、倒序 for 循环、或 Stream 过滤;
  3. 注意:CopyOnWriteArrayList 等线程安全集合允许 foreach 中删除,但本质是底层复制数组,性能较低,非并发场景不推荐。
相关推荐
Wx-bishekaifayuan2 分钟前
springboot房屋租赁系统11574-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·sql·spring·课程设计
三84419 分钟前
Fastjson 漏洞学习笔记 · 03 · 经典利用链:TemplatesImpl 与 JdbcRowSetImpl
java·web安全·fastjson
蜗牛互联网1 小时前
GPT-6.1 Sol迁移指南:从token单价转向每任务成本门禁
java·人工智能·后端·gpt
蜗牛互联网1 小时前
HSTU在Dynamo-Triton中的AOTI与KV缓存验收方法
java·人工智能·后端·缓存
Sirens.1 小时前
Java多线程实例:单例模式、阻塞队列、线程池与定时器
java·开发语言·单例模式
青山木1 小时前
秒杀系统设计(一):需求拆解与流量治理
java·数据库·redis·后端·架构
卓怡学长1 小时前
w195基于ssm“Fitfrend”个性化健身
java·intellij-idea
驭渊的小故事1 小时前
算法实战:滑动窗口、素数枚举与字符串模拟(三道经典题解)
java·算法
雪落漂泊1 小时前
Linux基本指令(下)
java·linux·服务器
Eloudy1 小时前
全文 - version.A - AMBA CHI Chip-to-Chip(C2C)
java·开发语言·数据库·gpu·chiplet