Java安全遍历集合需规避ConcurrentModificationException和NullPointerException,优先用增强for循环(只读)、Iterator.remove()(边遍历边删)、线程安全集合或同步块(多线程)、Stream无副作用操作(函数式)。

在Java中安全遍历集合,核心是避免并发修改异常(ConcurrentModificationException)和空指针异常(NullPointerException),同时兼顾线程安全与性能。关键不在于“能不能遍历”,而在于“用什么方式、在什么场景下遍历最稳妥”。
优先使用增强for循环(但注意不可删除元素)
增强for循环(for-each)底层调用Iterator,语法简洁、可读性高,适用于只读遍历场景。
- ✅ 安全:自动处理迭代器创建和hasNext()/next()调用,不易出错
- ❌ 危险:遍历中直接调用集合的remove()会触发ConcurrentModificationException
- ⚠️ 注意:若集合本身为null,会抛出NullPointerException——务必先判空
示例:
Listif (list != null) {
for (String name : list) {
System.out.println(name);
}
}
需要边遍历边删除?必须用显式Iterator.remove()
这是唯一被JDK明确支持的“遍历时安全删除”方式,Iterator的remove()方法会同步更新内部modCount,避免校验失败。
立即学习“Java免费学习笔记(深入)”;
- ✅ 正确:调用iterator.remove(),而非list.remove()
- ❌ 错误:在for-each中写list.remove(obj),或在while循环里用list.remove(index)
- ⚠️ 注意:每个next()后最多调用一次remove(),重复调用会抛IllegalStateException
示例:
Iteratorwhile (it.hasNext()) {
String s = it.next();
if (s.startsWith("A")) {
it.remove(); // 安全删除
}
}
多线程环境?选线程安全集合或加锁
普通ArrayList、HashMap等非线程安全,多线程读写+遍历极易出问题。不能靠“遍历方式”解决,得从数据结构或同步机制入手。
- ✅ 推荐:使用CopyOnWriteArrayList(适合读多写少)、ConcurrentHashMap(遍历时允许并发更新)
- ✅ 替代:对非安全集合加synchronized块,确保遍历与修改互斥
- ❌ 避免:仅用Collections.synchronizedList()包装后仍用增强for——迭代过程未同步,仍可能出错
正确示例(同步块):
synchronized (list) {for (String s : list) {
process(s);
}
}
函数式遍历(Stream)需留意短路与副作用
Java 8+ 的Stream.forEach()看起来简洁,但默认不保证顺序(并行流),且禁止在lambda中修改外部集合状态。
- ✅ 安全用法:只做无副作用操作,如打印、转换、过滤
- ❌ 危险用法:在forEach里add/remove原集合,或依赖遍历顺序却用了parallelStream()
- ⚠️ 更稳选择:filter/map后收集为新集合,再处理;删除逻辑仍回归Iterator
推荐替代写法:
List.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
基本上就这些。安全不是靠某一种写法包打天下,而是根据是否修改、是否多线程、是否允许延迟计算,选对工具链。判空、用对迭代器、分清集合类型——三者到位,遍历就很难翻车。










