在Java里如何安全地遍历集合_Java集合遍历安全策略说明

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

在Java中安全遍历集合,核心是避免并发修改异常(ConcurrentModificationException)空指针异常(NullPointerException),同时兼顾线程安全与性能。关键不在于“能不能遍历”,而在于“用什么方式、在什么场景下遍历最稳妥”。

优先使用增强for循环(但注意不可删除元素)

增强for循环(for-each)底层调用Iterator,语法简洁、可读性高,适用于只读遍历场景。

  • ✅ 安全:自动处理迭代器创建和hasNext()/next()调用,不易出错
  • ❌ 危险:遍历中直接调用集合的remove()会触发ConcurrentModificationException
  • ⚠️ 注意:若集合本身为null,会抛出NullPointerException——务必先判空

示例:

List list = getNames();
if (list != null) {
  for (String nam

e : list) {
    System.out.println(name);
  }
}

需要边遍历边删除?必须用显式Iterator.remove()

这是唯一被JDK明确支持的“遍历时安全删除”方式,Iterator的remove()方法会同步更新内部modCount,避免校验失败。

  • ✅ 正确:调用iterator.remove(),而非list.remove()
  • ❌ 错误:在for-each中写list.remove(obj),或在while循环里用list.remove(index)
  • ⚠️ 注意:每个next()后最多调用一次remove(),重复调用会抛IllegalStateException

示例:

Iterator it = list.iterator();
while (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 filtered = list.stream()
  .filter(s -> !s.isEmpty())
  .collect(Collectors.toList());

基本上就这些。安全不是靠某一种写法包打天下,而是根据是否修改、是否多线程、是否允许延迟计算,选对工具链。判空、用对迭代器、分清集合类型——三者到位,遍历就很难翻车。