背景
我们在日常开发中经常会使用List对数据进行排序、查找、截取等操作。接下来我们看下因为List截取导致的踩坑现场。
【问题描述】
在正常的业务需求迭代过程中,我们需要对List里的数据进行截取,并生成新的subList,然后对原有的list进行add/remove操作,结果导致我们的subList循环、add/remove等操作报错。
【故障现象】
抛出异常: Exception in thread "main" java.util.ConcurrentModificationException
问题复现:
首先我们先用一段代码复现问题根源,如下所示:
new一个list
增加三个测试数据
new一个sublist,并且list.sublist(0,1)存储新的数据
原list新增一个测试数据no4
循环sublist报错
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("no1"); list.add("no2"); list.add("no3"); List<String>subList=list.subList(0,1); list.add("no4"); System.out.println(subList); for(String str:subList){ System.out.println(str); }}
结果如下:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1087) at java.util.AbstractCollection.toString(AbstractCollection.java:454) at java.lang.String.valueOf(String.java:2994) at java.io.PrintStream.println(PrintStream.java:821) at com.jd.cyclePredict.Test.main(Test.java:22)
# 原因分析:- subList操作后会返回基于原数据对象的一个偏移量数据,创建一个新的对象SubList,如图所示:![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f7fe149002b43e99c07dbf83702c3ee~tplv-k3u1fbpfcp-watermark.image?)- 该对象是ArrayList的一个内部类,存储了原列表的一个偏移量,并且直接把原对象的modCount赋值给了内部类SubList的modCount,但针对原列表list的操作,该内部类是无感知的如下图所示:![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/57f6b3114c504dfd937c24018add648f~tplv-k3u1fbpfcp-watermark.image?)- 当遍历或操作内部类SubLIst时,会针对该类的modCount做一个check如图所示![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e960773752ad4faeae672d818c6c5cd8~tplv-k3u1fbpfcp-watermark.image?)- 如果sublist的modCount和原modCount不一致,会抛ConcurrentModificationException异常;![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6576ebb6fcf54b068df47194de7f0b64~tplv-k3u1fbpfcp-watermark.image?)# 解决办法**一行代码**:
List subList=new ArrayList<>(list.subList(0,1));
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d137533388241a48dd1881954e6d0e1~tplv-k3u1fbpfcp-watermark.image?)# 总结:- **截取操作后如果不需要原对象则可以进行该操作,但需求变更或者其他可能会导致后面有这么操作引起不必要的麻烦;**- **如果截取后有后续操作,建议创建一个新对象**原文:https://juejin.cn/post/7100508447992446989