Java中将ArrayList指定元素移至末尾报ConcurrentModificationException如何解决
问题原因
你遇到的ConcurrentModificationException是Java增强for循环(foreach语法)遍历集合时修改集合的典型报错:
- 增强for遍历
ArrayList的底层实现是调用集合自带的迭代器,迭代器初始化时会记录集合当前的修改次数modCount - 你直接调用
ArrayList的remove()、add()方法修改集合时,会更新集合的modCount,但迭代器持有的初始modCount不会同步更新 - 迭代器下一次执行遍历取值时,会校验两个
modCount是否一致,不一致就会直接抛出ConcurrentModificationException
额外说明:你代码中写的Box copy=i;没有实际作用,只是复制了对象的引用,并没有生成新的Box对象
正确实现方案
方案1:先收集符合条件的元素,批量操作(最推荐,性能最优)
遍历过程不修改原集合,先把需要移动的元素筛选出来,再统一修改,避免频繁触发集合结构变更:
// 1. 收集所有需要移动到末尾的Box List<Box> toMoveList = new ArrayList<>(); for (Box box : boxes) { if (box.mouseOver()) { toMoveList.add(box); } } // 2. 从原集合中批量移除待移动元素 boxes.removeAll(toMoveList); // 3. 把待移动元素批量追加到集合末尾 boxes.addAll(toMoveList);
方案2:使用迭代器自带的remove方法(单步修改场景)
如果需要在遍历过程中执行移除操作,必须用迭代器提供的remove()方法,它会同步更新迭代器内部的modCount,不会触发异常:
List<Box> toAddList = new ArrayList<>(); Iterator<Box> iterator = boxes.iterator(); while (iterator.hasNext()) { Box box = iterator.next(); if (box.mouseOver()) { iterator.remove(); // 迭代器自带的移除方法,会同步修改计数 toAddList.add(box); // 暂存待追加元素,遍历结束后统一添加 } } boxes.addAll(toAddList);
注意:即使使用迭代器,也不能在遍历过程中直接调用集合的add()方法,否则还是会触发修改计数不一致的问题
方案3:Java 8+流式写法(代码简洁)
如果项目使用Java 8及以上版本,可以用Stream流式API快速实现拆分合并:
// 按mouseOver条件拆分集合为两部分 Map<Boolean, List<Box>> partitionBoxes = boxes.stream() .collect(Collectors.partitioningBy(Box::mouseOver)); // 不需要移动的元素在前,需要移动的元素在后,合并为新集合 boxes = Stream.concat(partitionBoxes.get(false).stream(), partitionBoxes.get(true).stream()) .collect(Collectors.toList());
补充:普通for倒序遍历(可行但不推荐)
倒序遍历可以避免正序删除时的索引错位问题,不会抛异常,但频繁修改集合性能较差,仅适合小体量集合使用:
for (int i = boxes.size() - 1; i >= 0; i--) { Box box = boxes.get(i); if (box.mouseOver()) { boxes.remove(i); boxes.add(box); } }
内容的提问来源于stack exchange,提问作者Phil_Software
相关产品推荐
相关产品推荐

