Java迭代器遍历时直接修改集合会触发ConcurrentModificationException,应使用迭代器自身的remove()安全删除;需增删改时改用ListIterator;复杂逻辑可先收集操作项再批量处理;特殊场景可用CopyOnWriteArrayList等线程安全集合。

Java 中在迭代器遍历过程中修改集合(如 add、remove、clear)会触发 ConcurrentModificationException,这不是多线程并发问题,而是“快速失败”(fail-fast)机制的主动检测结果。关键在于:**不能在使用普通迭代器(Iterator 或增强 for 循环)遍历时直接调用集合自身的修改方法**。
用迭代器自身的 remove() 安全删除
这是最常用、最推荐的方式。迭代器的 remove() 方法被设计为与当前遍历状态同步,能安全地移除上一次 next() 返回的元素。
- 必须在调用
next()之后、且仅调用一次remove(),否则抛IllegalStateException - 不能用于添加元素(
add()不在标准Iterator接口中) - 适用于
ArrayList、LinkedList、HashSet等大多数集合
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("c".equals(s)) {
it.remove(); // ✅ 安全
}
}
// list 变为 ["a", "b", "d"]
需要增删改时改用 ListIterator
ListIterator 是 Iterator 的子接口,支持双向遍历和在遍历中安全插入、删除、替换元素。
-
add(E):在当前游标位置前插入元素(不影响后续遍历) -
set(E):替换上一次next()或previous()返回的元素 -
remove():同普通迭代器,但更灵活(配合双向移动) - 仅适用于
List实现(如ArrayList、LinkedList)
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
ListIterator<Integer> lit = nums.listIterator();
while (lit.hasNext()) {
Integer x = lit.next();
if (x == 2) {
lit.set(20); // ✅ 替换为 20
lit.add(25); // ✅ 在 20 后插入 25
}
}
// nums 变为 [1, 20, 25, 3]
避免修改、改用收集+批量操作
如果逻辑复杂(比如要根据条件动态增删多个元素),可先用临时集合记录待操作项,遍历结束后统一处理。
立即学习“Java免费学习笔记(深入)”;
- 遍历时只读,不调用任何修改方法,彻底规避异常
- 适合需要多次判断、跨索引关联操作的场景
- 注意内存开销(临时集合大小与原集合相当)
List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
List<String> toRemove = new ArrayList<>();
List<String> toAdd = new ArrayList<>();
for (String s : list) {
if (s.startsWith("a")) {
toRemove.add(s);
} else if (s.length() > 6) {
toAdd.add(s.toUpperCase());
}
}
list.removeAll(toRemove);
list.addAll(toAdd); // ✅ 批量操作,安全
特殊场景:用线程安全集合或复制快照
若确实需在遍历中自由修改(尤其多线程环境),可考虑替代方案,但需权衡性能与语义:
-
CopyOnWriteArrayList:遍历时操作底层数组副本,写操作开销大,适合读多写少 -
ConcurrentHashMap:支持弱一致性遍历,forEach不抛 CME,但可能看不到最新修改 - 手动复制:
new ArrayList(originalList)遍历副本,修改原集合——注意这不是原子操作,需自行保证业务一致性


















