如何在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的读写操作都在同一锁下执行,避免并发修改:
- 在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(); // 全局锁 }
- 修改任务代码,所有涉及列表的操作加锁:
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
相关产品推荐
相关产品推荐

