在迭代List时,如果不通过iterator去修改list,那么将得到ConcurrentModificationException。
所以一般自己写的代码都会尽力避免这样的事情。但如果迭代和修改被分布在不同类的方法里,那么问题就很隐蔽了。
有一个同事写了一段这样的代码:
//代码段1
List<List<A>> slicedList = ListUtil.sliceList(someList,size);
someList.clear();
for(List<A> subList: slicedList ){
//迭代subList进行其他处理
}
拿到一个大的List,然后按照给定的大小拆分成多个小的list,再清空原有的list,然后循环多个小的list。
这货自己能产生ConcurrentModificationException吗?普通的实现下,肯定不会有问题。
但是悲剧还是出现了。
//代码段2
public static <T> List<List<T>> sliceList(final List<T> list, int batchSize) {
List<List<T>> result = new ArrayList<List<T>>();
if (CollectionUtils.isEmpty(list) || 0 >= batchSize) {
return result;
}
final int n = (list.size() + batchSize - 1) / batchSize;
for (int i = 0; i < n; i++) {
result.add(list.subList(i * batchSize, Math.min((1 + i) * batchSize, list.size())));//这里
}
return result;
}
sliceList的实现导致代码段1出现ConcurrentModificationException。
原因:sliceList方法调用了list.subList,
subList其实不过是原有somelist的视图,但是subList保存了somelist的modCount(修改操作的数量)。
在代码段1中,
sliceList方法之后clear方法被调用了,导致someList的modCount发生了变化。
在随后的“迭代subList进行其他处理”中,subList将被迭代,而subList不过是somelist的视图,它持有someList的modcount。
迭代subList时,迭代器的next方法将被调用,而next的第一步就是checkForComodification(),它检查someList的modCount是否与自身持有的modCount,很显然因为视图中记录了modCount之后,代码段1中clear操作已经将modCount增加了,所以不相等。
再来捋一捋:
1、sliceList方法先执行,其中的subList方法返回了someList的视图,视图记录了someList的modCount。具体参考SubList的构造方法。
2、clear方法被执行,modCount加1;
3、“迭代subList进行其他处理”开始执行,开始遍历subList方法返回的视图,该视图的next方法把自己持有的宝贝modCount与someList的modCount进行了比较。发现了不一致,抛出异常。具体参考AbstractList中Itr的next方法
综上,sliceList的实现是一个悲剧。它的实现埋下了一个隐秘的大坑。其实,在代码段2中只要subList替换成一个new一个list即可避免这样的问题。具体实现很简单。
List的具体实现可参考:AbstractList的方法subList和内部类SubList。
分享到:
相关推荐
java.util.ConcurrentModificationException 异常问题详解1
NULL 博文链接:https://chenlinbo.iteye.com/blog/832335
java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除。 则使用会报以下异常: Java.util....
主要介绍了出现java.util.ConcurrentModificationException 问题及解决办法的相关资料,需要的朋友可以参考下
axis1.4补丁包,解决jdk1.8高并发报ConcurrentModificationException问题,该jar包重新编译jar包的一个class文件,线上环境通过
今天小编就为大家分享一篇关于Java源码解析ArrayList及ConcurrentModificationException,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
Map在遍历时候通常 现获得其键值的集合Set,然后用迭代器Iterator来对Map进行遍历。
Spring数据mongodb测试 在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException
axis1.4 spring3.0 集成 实现 web service 服务端, axis1.4 客户端认证,授权,访问日志记录,集成spring 解决 PHP 调用web service 无法认证,和解析soap 模板
fastJson的全部资料,包括源码、开发需要用到的jar包和html格式的文档。
这里面包含了大部分的软件测试的专业术语,希望对你有用
java.util.ConcurrentModificationException: mutation occurred during iteration [error] scala.collection.mutable.MutationTracker$.checkMutations(MutationTracker.scala:43) [error] scala.collection....
Iterator遍历中 ConcurrentModificationException异常
CopyOnWriteArraySet 是Java中的一个线程安全的集合类,它实现了 Set 接口并使用了"写时复制"的机制。 下面是关于 CopyOnWriteArraySet 的一些重要信息: 线程安全性:CopyOnWriteArraySet 是线程安全的,可以在多...
iterator和listIterator方法是快速失败的 :如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove种或add方法,迭代器都将抛出一个ConcurrentModificationException 。 因此,面对...
快速失败的迭代器抛出ConcurrentModificationException一个最大努力的基础上。 因此,这将是错误的,写一个程序,依赖于此异常的它的正确性: 迭代器的快速失败行为应该仅用于检测bug。 这个类是成员的Java集合框架
然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中...
写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出bbbhhtccc。 3.数据类型之间的转换 如何将数值型字符转换为数字(Integer,Double) 如何将数字...