java并发集合的iterator遍历不抛concurrentmodificationexception,但不可调用remove();应使用removeif()、compute()等专用线程安全方法修改集合。

Java 中用 Iterator 遍历并发集合(如 ConcurrentHashMap、CopyOnWriteArrayList 等)本身不会报 ConcurrentModificationException,但关键在于:**不能在遍历过程中通过非线程安全方式修改集合**,且要理解不同并发集合的迭代器行为差异。
看清集合类型:不是所有“并发集合”都支持安全迭代
Java 的并发集合实现机制不同,迭代器的线程安全性也不同:
-
ConcurrentHashMap:迭代器是弱一致性(weakly consistent)的,不抛CME,可容忍并发修改,但不保证反映最新修改(可能跳过或重复某些元素),也不支持remove()方法(调用会抛UnsupportedOperationException)。 -
CopyOnWriteArrayList/CopyOnWriteArraySet:迭代器基于创建时的数组快照,完全不可变,因此遍历时修改原集合(增删)完全不影响当前迭代,也不会报错;但迭代器自己的remove()也抛UnsupportedOperationException(因为快照只读)。 -
ConcurrentLinkedQueue/ConcurrentLinkedDeque:迭代器也是弱一致的,不抛CME,但同样不支持remove(),且遍历结果可能不包含某些刚入队/已出队的元素。
避免手动调用 iterator.remove() —— 大多数并发集合不支持
并发集合的迭代器通常将 remove() 设为未实现操作。若你在循环中写:
for (Iterator
String s = it.next();
if (s.isEmpty()) it.remove(); // ❌ 运行时报 UnsupportedOperationException
这是常见错误。正确做法是:
- 对
CopyOnWriteArrayList:改用removeIf()(线程安全,内部重建数组); - 对
ConcurrentHashMap:用computeIfPresent()、remove(key, value)或keySet().removeIf(...)(JDK 8+,线程安全); - 若必须边查边删,考虑先收集待删 key/element,再批量删除(如
map.keySet().removeAll(toRemove))。
遍历时修改集合?用专用方法替代直接操作
不要在 for-each 或显式 Iterator 循环里调用 add()、put()、remove() 等修改方法。推荐方式:
-
批量处理优先:用
forEach()+ 无副作用函数; -
条件删除用内置方法:
list.removeIf(x -> x == null)、map.entrySet().removeIf(e -> e.getValue() == null); -
需要原子性更新:用
compute()、merge()、replace()等 CAS 安全方法; -
极端场景需强一致性:加锁(如
synchronized(map))并改用普通HashMap+ 普通迭代器(牺牲并发性换一致性)。
小结:不报错 ≠ 可随意改,关键是选对集合 + 用对 API
并发集合的迭代器设计目标是“不崩溃”,而非“实时精确”。只要做到三点就不会报错:
- 用对集合类型(别把
ArrayList错当并发集合用); - 遍历时只读访问,删除/新增走专用线程安全方法;
- 不调用迭代器自身的
remove()。
这样,Iterator 遍历就能稳定运行,既发挥并发优势,又避开陷阱。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











