社交媒体

遍历集合的 4 种姿势

遍历集合的 4 种姿势

钩子:删个元素而已,怎么就炸了

购物车里 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));

25-遍历4种姿势对比图.png

正题:为什么增强 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


橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。