Java迭代修改LinkedList触发ConcurrentModificationException的解决方案
针对迭代修改集合触发异常的优化方案
1. 用迭代器完成所有修改操作
Java集合的fail-fast迭代器会在迭代期间检测到非迭代器发起的结构修改(比如直接调用列表的remove/add)时抛出异常。解决办法是所有对列表的修改都通过当前迭代器执行:
for (LinkedList<State> list : lists) { ListIterator<State> iter = list.listIterator(); while (iter.hasNext()) { State curr = iter.next(); // 安全移除当前元素 iter.remove(); // 向当前位置插入新元素 iter.add(new State(...)); // 向其他列表插入时,也要用对应列表的迭代器操作 } }
这种方式完全符合Java集合的设计规范,不需要额外开发成本。
2. 分离迭代与修改流程
可以先遍历所有列表,收集本次迭代需要执行的所有修改指令(比如要移除的元素、要插入的元素及目标列表),等遍历完成后统一执行修改:
// 定义简单的修改指令类 record ModifyCmd(Type type, List<State> target, State data) {} enum Type { REMOVE, INSERT } // 收集指令 List<ModifyCmd> cmds = new ArrayList<>(); for (LinkedList<State> list : lists) { for (State s : list) { if (需要移除s) { cmds.add(new ModifyCmd(Type.REMOVE, list, s)); } if (需要插入新元素到目标列表) { cmds.add(new ModifyCmd(Type.INSERT, targetList, new State(...))); } } } // 执行修改 for (ModifyCmd cmd : cmds) { if (cmd.type() == Type.REMOVE) { cmd.target().remove(cmd.data()); } else { cmd.target().add(cmd.data()); } }
这种方式避免了迭代时修改集合,逻辑清晰,内存开销远小于创建全新列表数组。
3. 改用队列类处理元素流转
如果你的算法逻辑是“取出元素处理→生成新元素放入对应容器”,可以用ArrayDeque这类队列替代链表。每次迭代时,取出当前队列的所有元素处理,生成的新元素直接放入目标队列,天然避免迭代修改冲突:
ArrayDeque<State>[] queues = new ArrayDeque[yourIndexCount]; // 初始化队列... while (!isAcceptableState()) { for (ArrayDeque<State> q : queues) { int batchSize = q.size(); for (int i = 0; i < batchSize; i++) { State curr = q.poll(); // 处理curr,生成新状态 queues[targetIndex].offer(newState); } } }
这种方式代码简洁,完全不会触发ConcurrentModificationException,性能也更优。
4. 谨慎使用并发集合(视场景而定)
如果你的修改操作不要求实时可见,可考虑CopyOnWriteArrayList。它的迭代器基于集合快照,迭代时修改集合不会抛异常,但每次修改都会复制底层数组,适合读多写少的场景。如果你的迭代中修改频繁,内存开销可能比创建新列表还大,需谨慎选择。
内容的提问来源于stack exchange,提问作者Ignat Insarov
相关产品推荐
相关产品推荐

