A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 大山哥哥 于 2017-7-24 17:27 编辑

集合迭代—神秘的倒数第二个元素
引言
学过基础班的同学估计都知道什么叫并发修改异常,即ConcurrentModificationException,之所以产生这个异常,是因为在迭代器遍历集合的过程中,是不允许集合对元素进行增删的,但却可以用迭代器去增删元素,如果你在迭代器遍历集合的时候,强行让集合对元素进行增删就会产生并发修改异常,比如下面的代码就会产生并发修改异常:
                              


这是因为在迭代器遍历集合的时候,让集合对元素进行了增删。
如下代码就不会产生并发修改异常:

这是因为迭代器遍历集合的时候,让迭代器对元素进行了增删。这也是并发修改异常这个类被设计出来的原因,下面是我从API上截取出来的类的介绍

问题
并发修改异常,这虽然是一个众所周知的知识点,但是问题却来了,当我们如果我们遍历到集合的倒数第二个元素的时候,让集合删除任意一个元素,并不会出现并发修改异常,我们去观察这一下现象,代码如下:


为什么会出现这样的问题呢?我们又去API查看此类的时候我们发先API中有如下的解释

并发修改异常这个类,并不能硬性保证是否出现并发修改,但是会尽最大努力,因此他的不能硬性保证就表现在了“倒数第二个元素”。
探究
但是仅仅只得到了这样一个结论,我还是不甘心。所以我们一起去看看迭代器的源码,为什么在倒数第二个元素就不能保证发生并法修改呢?为了方便大家理解什么是迭代器,我给大家做了一个迭代器和集合的简易模型。

通过上面的代码我们可以看出,迭代器的实现类是集合的内部类,迭代器其实就是在获取的集合底层数组存储的元素,每next()一次,索引就增加一次,这样就能把集合里面的元素都遍历出来了。
但是JDK中提供的迭代器的源码,并没有如此简单,请大家仔细查看下面的源码,必须明白每一个用红色矩形圈起来的代码的意思。

查看源码之后并且理解之后,我们再来回想我们的并发修改异常产生的原因参照下面代码

当我们遍历到第二个元素,删除完毕“abc2”元素的时候,modCount就自增了一次,接着迭代器调用hasNext()方法判断cursorsize是否相同,结果发现不同,接着调用next()的时候发现expectedModCountmodCount不同,所以就报出了并发修改异常。
现在我们再来看看为什么遍历到倒数第二个元素的时候,删除就不会报出并发修改异常呢?参照如下代码:

当我们遍历到倒数第二个元素,cursor的值是3,删除完毕“abc4”元素的时候,modCount就自增了一次,而size也从4变成了3,接着迭代器调用hasNext()方法判断cursorsize是否相同,结果发现相同都是3,所以返回false,那么while循环就会终止,就不会再继调用next()了。所以就不需要比较expectedModCountmodCount是否相同了,所以就不会再报出并发修改异常了。。

19 个回复

倒序浏览
支持支持,写的很用心~
回复 使用道具 举报
小鲁哥哥 发表于 2016-12-31 17:01
支持支持,写的很用心~

谢谢小鲁兄
回复 使用道具 举报
小鲁哥哥 发表于 2016-12-31 17:01
支持支持,写的很用心~

谢谢小鲁兄
回复 使用道具 举报
小鲁哥哥 发表于 2016-12-31 17:01
支持支持,写的很用心~

谢谢小鲁兄
回复 使用道具 举报
哇。。好喜欢这类型的贴。。赞下
回复 使用道具 举报
好贴,已顶!
回复 使用道具 举报
赞一个..............
回复 使用道具 举报
赞一个赞一个
回复 使用道具 举报
好贴,来赞一个
回复 使用道具 举报
好东西,收藏,赞
回复 使用道具 举报
写的好用心好详细,感谢楼主分享这么好的东西给我们
回复 使用道具 举报
迭代器的并发修改异常,写的很详细,
回复 使用道具 举报
嘿嘿嘿 前天刚学了.!!! 要用ListItrator用迭代器去添加元素~
回复 使用道具 举报
赞一个赞一个
回复 使用道具 举报
newu 黑马帝 2017-1-15 18:12:52
16#
好东西,收藏,赞
回复 使用道具 举报
老师拿枪指着我们脑袋让我们来顶贴,不顶不让毕业{:8_470:}
回复 使用道具 举报
希望对大家有所帮
回复 使用道具 举报
不错,迭代器修改并发异常提的确值得注意。还有迭代删除集合元素时,注意顺序
回复 使用道具 举报
这样的技术贴牛批
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马