遍历arraylist时直接调用remove()会抛concurrentmodificationexception,因迭代器的fail-fast机制通过modcount与expectedmodcount校验实现;正确做法是用iterator.remove()、removeif()或copyonwritearraylist。

为什么遍历 ArrayList 时调用 remove() 会抛 ConcurrentModificationException
这不是线程安全问题,而是迭代器的「快速失败」(fail-fast)机制在起作用。每个 ArrayList 内部维护一个 modCount 计数器,记录结构修改次数;而它的 Iterator 在创建时会把当前 modCount 快照为 expectedModCount。只要后续有非迭代器自身的修改(比如直接调用 list.remove()),两者就会不一致,next() 或 hasNext() 就立刻抛异常。
用 Iterator.remove() 安全删除元素
这是最直接、最符合设计意图的解法——让迭代器自己负责同步更新 expectedModCount。
- 必须在调用过
next()后立即调用,否则抛IllegalStateException - 不能和
list.remove()混用,否则仍会触发检查 - 只适用于单次删除;想删多个,得在每次
next()后判断再调remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("tmp")) {
it.remove(); // ✅ 正确
}
}
用 removeIf() 替代手动遍历(Java 8+)
底层其实也是用的 Iterator.remove(),但封装得更简洁,语义也更清晰。
- 适用于所有实现了
Collection的类(ArrayList、LinkedList、HashSet等) - 不能在 lambda 中修改集合本身,否则行为未定义
- 性能上比手写循环略好一点(避免了重复的类型检查和边界判断)
list.removeIf(s -> s.startsWith("tmp")); // ✅ 推荐用于简单条件
用 CopyOnWriteArrayList 应对高频读 + 低频写场景
它不是解决「遍历时删除」的通用方案,而是换了一套机制:每次写操作都复制整个数组。所以迭代器持有的是快照,不受后续修改影响。
立即学习“Java免费学习笔记(深入)”;
- 迭代期间允许任意修改,完全不会抛
ConcurrentModificationException - 写操作开销大(尤其是大集合),内存占用翻倍,不适合写多或集合大的情况
- 迭代器无法反映「本次迭代开始后」的修改,适合「读远多于写」且对实时性不敏感的场景(如监听器列表)
List<String> list = new CopyOnWriteArrayList<>(); // 遍历时 remove() 不会出错,但代价是复制数组
真正容易被忽略的是:这个异常根本不是并发导致的,单线程里反复调用 list.remove() 和 iterator.next() 一样会崩。关键不在「谁在改」,而在「谁在改」和「谁在遍历」是否走同一套协议。










