You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ListIterator中重建Iterator?解决ConcurrentModificationException

解决ConcurrentModificationException并实现按需重建迭代器的方案

1. 异常根源说明

  • sublist()返回的是原列表的视图,而非独立副本。只要原列表或其他基于原列表的sublist发生结构变更(添加、删除、清空元素等),所有关联sublist的迭代器都会因内部修改计数(modCount)不匹配抛出异常。
  • 你代码中循环提交任务到线程池,即使是单线程池,后续任务也可能在之前的任务迭代过程中修改原列表或list2的结构,导致第二次迭代list1时,list2的迭代器因状态失效抛出异常。另外原代码内层循环存在错误:String s2 = list2.next();应该调用iter2.next(),否则未使用创建好的迭代器,会加剧迭代状态混乱。

2. 具体解决方案

方案一:为list2创建独立副本(最稳妥)

直接将list2转为独立的ArrayList副本,彻底脱离与原列表的关联,这样其他线程修改原列表不会影响到副本:

executorService.execute(() -> {                              
    List<String> list = Storage.list;
    for (ListIterator<String> listIterator = list.listIterator(); listIterator.hasNext(); ) {
        String s = listIterator.next();
        listIterator.set(s);
        // 每次迭代list1时创建list2的独立副本,确保迭代不受原列表影响
        List<String> list2 = new ArrayList<>(Storage.list2);
        for (Iterator<String> iter2 = list2.iterator(); iter2.hasNext(); ) {
            String s2 = iter2.next();
            // 业务逻辑
        }
    }
});

如果需要每次迭代list1时都获取list2的最新状态,就在每次循环内创建副本即可。

方案二:同步所有列表操作(保留视图特性)

如果必须保留sublist的实时视图特性,需保证所有对原列表、list、list2的读写操作都在同一锁下执行,避免并发修改:

  1. 在Storage类中定义全局锁对象:
public class Storage {
    public static List<String> originalList;
    public static List<String> list = originalList.subList(0, 5);
    public static List<String> list2 = originalList.subList(5, 10);
    public static final Object LIST_LOCK = new Object(); // 全局锁
}
  1. 修改任务代码,所有涉及列表的操作加锁:
executorService.execute(() -> {                              
    synchronized(Storage.LIST_LOCK) {
        List<String> list = Storage.list;
        for (ListIterator<String> listIterator = list.listIterator(); listIterator.hasNext(); ) {
            String s = listIterator.next();
            listIterator.set(s);
            List<String> list2 = Storage.list2;
            // 每次迭代list1时重新创建list2的迭代器
            for (Iterator<String> iter2 = list2.iterator(); iter2.hasNext(); ) {
                String s2 = iter2.next();
                // 业务逻辑
            }
        }
    }
});

同时,所有修改原列表、list、list2结构的代码,也必须在Storage.LIST_LOCK锁下执行,确保迭代过程中无其他线程修改列表结构。

方案三:改用并发安全列表实现

如果允许替换原列表的实现,使用CopyOnWriteArrayList,它的迭代器基于快照创建,不会因并发修改抛出异常:

public class Storage {
    public static List<String> originalList = new CopyOnWriteArrayList<>();
    public static List<String> list = originalList.subList(0, 5);
    public static List<String> list2 = originalList.subList(5, 10);
}

注意:CopyOnWriteArrayList适合读多写少的场景,每次写操作会复制整个列表,性能开销较大;它的sublist仍是视图,修改会同步到原列表。

内容的提问来源于stack exchange,提问作者janusz j

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:45:35