钩子:删个元素而已,怎么就炸了
购物车里 5 件商品,要删掉已下架的那件。你的直觉写法:
for (String goods : cart) {
if (goods.equals("已下架商品")) {
cart.remove(goods); // 运行时炸:ConcurrentModificationException
}
}
编译通过,一运行就报 ConcurrentModificationException。新手三大迷惑报错之一,今天连根拔起。
核心:4 种姿势逐一上手
姿势 1:普通 for(只有 List 有下标,Map 没有)
for (int i = 0; i < list.size(); i++) {
System.out.println(i + " -> " + list.get(i));
}
姿势 2:增强 for——最常用,写法最短
for (String s : list) {
System.out.println(s);
}
姿势 3:迭代器 Iterator——边遍边删的唯一正解
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
Integer n = it.next();
if (n % 2 == 0) {
it.remove(); // 注意:是迭代器的 remove,不是集合的
}
}
姿势 4:forEach + Lambda——JDK 8 起,一行搞定
list.forEach(s -> System.out.println(s));

正题:为什么增强 for 里不能删?
增强 for 只是迭代器的"语法糖"——编译器把它翻译成 Iterator 那套。规矩是:遍历期间集合的结构只能由迭代器自己改。
你在循环里直接 cart.remove(...),集合自己动了;下一轮迭代器一对账,"元素数量对不上",当场抛 ConcurrentModificationException。
所以解药就一句话:要边遍边删,用 it.remove()——删的是刚取出的那个元素,迭代器自己记了账,不会翻脸。
Map 的遍历单独说:不能用普通 for(没下标),标准姿势是 entrySet,一次把 key 和 value 都拿到:
for (Map.Entry<String, Integer> e : score.entrySet()) {
System.out.println(e.getKey() + " 的成绩是 " + e.getValue());
}
速查表
| 姿势 | 适用 | 能否边遍边删 | 简洁度 |
|---|---|---|---|
| 普通 for | List 且要下标 | 能但别用(下标会错位) | ★★ |
| 增强 for | List / Map.keySet / 数组 | 不能 | ★★★★★ |
| Iterator | 要边遍边删的场景 | 能(it.remove) | ★★★ |
| forEach + Lambda | 简单遍历 | 不能 | ★★★★★ |
一句话结论:只读用增强 for,要删用 Iterator,其他的都是花活。
本文示例代码均已本地实测通过,可直接运行。
对应文件:TraverseDemo.java
橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。