商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Java 中如何处理集合的并发修改异常

Java 中如何处理集合的并发修改异常

  发布于2026-07-05 阅读(0)

扫一扫,手机访问

ConcurrentModificationException 可不是多线程的专利——单线程遍历集合时,如果你手痒直接调用 list.remove() 改动了集合结构,它照样会跳出来。背后的核心机制是 fail-fast:每次结构修改(比如 add、remove)都会更新 modCount,而迭代器内部记录着自己的 expectedModCount,两者一旦对不上,二话不说就抛异常,防止数据错乱甚至数组越界。那么,怎么避开这个坑?下面几种解法各有适用场景,挑对的那款就好。

用 Iterator.remove() 安全删除

这是最基础也最稳妥的单线程解法。关键在于,Iterator.remove() 和集合自带的 remove() 完全是两码事——它会在内部同步更新状态,所以不会触发校验失败。

  • 必须紧跟在 next() 调用之后、下一次 next() 之前调用,否则会抛 IllegalStateException
  • 增强 for 循环里用不了,因为拿不到 Iterator 对象,得手动写迭代器循环。
  • 写法示例:it.remove() ✅;list.remove(item)

优先用 removeIf() 批量过滤

Ja va 8 之后,官方推荐用 removeIf(),一句话搞定,底层走的也是安全的迭代逻辑,比手写 Iterator 更简洁、更不容易出错。

  • 适用于 ListSet 等实现了该方法的集合。
  • 写法:list.removeIf(x -> x.startsWith("A")),语义清晰,一行解决。
  • 比起“先收集再删”,它更省内存,也不用担心索引偏移的问题。

多线程场景选对并发集合

别一根筋用 synchronized 硬锁,优先考虑专为并发设计的集合,效果和性能都好得多。

  • CopyOnWriteArrayList:适合读多写少的场景(比如监听器列表),遍历时可以安全增删,但每次写都会复制整个数组,写频繁时 GC 压力不小。
  • ConcurrentHashMap:高并发 Map 操作的首选,它的 keySet()values() 迭代不会抛 CME。注意像 computeIfAbsent 这样的方法要按文档使用。
  • 尽量避开 Collections.synchronizedList:它只是给每个方法加了锁,遍历时你仍得手动同步整个代码块,容易漏掉,而且性能一般。

复杂逻辑用暂存+统一删除

如果判断条件太复杂,用不了 removeIf,又不想换集合类型,那这个兜底方案就很实用。

  • 新建一个临时集合(比如 ArrayListHashSet),用来存放待删除的元素。
  • 遍历原集合时只做判断和收集,千万别去动原集合的结构。
  • 遍历结束后,统一调用 list.removeAll(toRemove),或者逐个 remove()
  • 小技巧:如果原集合是 HashSet,用 HashSet 来存待删项,能让 removeAll 跑得更快。
本文转载于:https://www.php.cn/faq/2739603.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注