网站首页 服务项目 客户案例 我们优势 主机域名 关于黑羽 联系黑羽

资讯中心
了解网站建设资讯引领网站建设开发方向
可恶!简单的删除集合中的元素竟然报错

前言

什么是快速失败:fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
运行如下代码,即可出现异常:
控制台会输出如下异常:

为什么要报这个错?途中出错的地方是ArrayList中的代码,定位到该处代码:
modCount是这个集合修改的次数,这个属性来自AbstractList,而我们的ArrayList是继承了该抽象类的。
expectedModCount又是啥呢?当我们进行遍历时候debug一下发现进行forEach循环的时候其实走了下面这个方法iterator,而且遍历这个底层还是走的hasNext方法
判断是否有下一个元素
next()方法用于获取元素
点进这个new Itr(),惊喜的发现原来这个expectedModCount是在这里被赋值的而且和modCount一样
接下来看下ArrayList的remove()方法,其对modCount进行了增加,这是导致报错的原因
上面的next()方法这有调用一个checkForComodification()方法,下面贴一下这方法的代码
ArrayList里面remove()方法进行了modCount++操作,原来是我们对集合进行操作后改变了modCount导致上面代码成立,从而抛出异常
但是当我们使用Itr类的remove,也就是如下代码进行对元素改动时,不会抛出ConcurrentModificationException异常
与ArrayList的remove()方法不同的是,该remove()方法调用ArrayList.this.remove(lastRet);后显然modCount++了,但是马上又让expectedModCount = modCount就是这样才不会抛出异常。
梳理整个流程:
1、for循环遍历实质上调用的是Itr类的方法进行遍历(Itr类实现了Iterator)
2、Itr类在构造的时候会将ArrayList的modCount(实际上modCount是AbstractList的属性,但是ArrayList继承了AbstractList)赋值给Itr类的expectedModCount
3、for循环中调用的remove()方法时ArrayList的,这个方法会对modCount进行++操作
4、remove方法调用后,继续遍历会调用Itr的next()方法,而这个next()方法中的checkForComodification()方法会对modCount和expectedModCount进行对比,由于remove方法已经操作过modCount因此这俩个值不会相等,故报错。
如何改进?
1、可以使用Itr中的remove方法进行改进,改进代码如下
2、使用CopyOnWriterArrayList来代替Arraylist,它对ArrayList的操作时会先复制一份数据出来操作完了再将其更新回去替换掉旧的,所以CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。这是采用了CopyOnWriterArrayList的fail-safe机制,当集合的结构被改变的时候,fail-safe机制会在复制原集合的一份数据出来,然后在复制的那份数据遍历,fail-safe机制,在JUC包的集合都是有这种机制实现的。
虽然fail-safe不会抛出异常,但存在以下缺点
1、复制时需要额外的空间和时间上的开销。
2、不能保证遍历的是最新内容。
对于fail-fast机制,我们要操作List集合时可以使用Iterator的remove()方法在遍历过程中删除元素,或者使用fail-safe机制的CopyOnWriterArrayList,当然使用的时候需要权衡下利弊,结合相关业务场景。

网页制作的服务
网站开发服务
自贡黑羽网络品牌建站
自贡本地做网站就选黑羽网络
Tel:0813-5104030 15348110304 QQ或微信:22232591
地址:四川省自贡市自流井区丹佳大街泰丰大厦写字楼19楼10号
Copyright © 2018-2058 自贡黑羽网络科技有限公司 All rights reserved. ICP备案号 : ICP备12014994号-1 技术支持:黑羽网络
自贡网站建设,网页设计制作与开发,自贡网络公司推荐品牌,关键词: 自贡网站制作 自贡做网站 自贡网络公司 自贡网页制作 自贡做网站 网站地图xml 网站地图html 网站地图txt