Iterator中的Itr类(ArrayList)

版权声明:知识本无差别,何必在意你我? https://blog.csdn.net/qq_33774822/article/details/82757071

填坑了,填坑了。

上一篇留的问题,大家都有看吗,哪怕没看,那有放在IDEA中执行吗?

好,我就认为大家都有思考,下面就是到了激动人心,公布答案的时间啦。

按照日常的套路来,答案当然是错误的,恭喜你,都知道答案啦。

好,我们言归正传,来看看他为什么错,错在哪里?

先看代码:

扫描二维码关注公众号,回复: 3599418 查看本文章

很明显,他是在第36行报错的,也就是在遍历完数值为3的数据报错的。让我们往前顺顺,为什么遍历完第二个元素就报错了,因为他遍历完数值为3的数据后,往list里面增加了一个数值为12的数据。

那我们把遍历里面的if判断去掉试试,答案是肯定正确的。那我们找到了原因,也就是在遍历的时候添加了一个元素,所以导致了他错误。

 我们看一下ArrayList中的源码,他在add方法里面做了什么,导致了他在遍历的时候报错。

图一:

图二:

图三:

 图四:

上面的四幅图都是层级调用的关系, 也就是在执行确定按钮的时候,先确定list数组的大小,ensureCapacityInternal方法,如果为空数组,就取ArrayList中的常量DEFAULT_CAPACITY作为容器大小,然后增加修改次数modCount,最后比较最小容量和数组长度的大小,考虑扩容的情况。从刚才的叙述来看,他只是增加修改次数modCount,并考虑是否扩容。

那我们来看一下modCount是什么,以及在哪里使用了,让我们回到这个题目。

在第33行,在list数组上定义了一个iterator,我们跟到源码看一下

也就是他创建了一个Itr类,而这个类包括cursor,lastRet,expectedModCount三个变量,hasNext,next,remove三个方法。

那么我们来科普一下这三个变量分别代表什么意思,cursor表示下一个要访问元素的下标,lastRet表示上一个访问元素的下标,expectedModCount表示期望修改次数。

next方法和remove方法在开始的地方都调用了checkForComodification方法,那该方法里面就是判断modCount和expectedModCount次数是否一样,如果不一样,就抛出异常,很明显,我们开始运行代码时的报错信息与该异常相吻合,那其实我们就知道了他其实是在该处抛出的异常信息导致程序报错,那也就是modCount和expectedModCount是不一样的。

下面我们从头开始整理一下整个流程,断点调试一下整个代码。

首先他先构造一个数组,并往里面增加了1,3,2数据,在上面已经看过add方法里面有什么,在add方法里面有增加modCount的值,所以在这里modCount已经为3啦。接着定义了一个iterator,刚才我们知道啦其实也就是新建了一个Itr类,那我们看下在33行结束后,iterator的值是什么

下一个要访问的下标cursor为0,上一个要访问的下标lastRet为-1,预计期望修改次数expectedModCount为3,其实也就是等于他的数组大小size,也就是3。

接着进入35行开始迭代数组,执行hasNext方法,cursor为0,不等于size,为true,继续执行next方法,先判断modCount(也就是3)是否与expectedModCount(也就是3)相等,答案是相等的,再输出该数值1。

接着再进入35行开始迭代数组,执行hasNext方法,cursor为1,不等于size,为true,继续执行next方法,先判断modCount(也就是3)是否与expectedModCount(也就是3)相等,答案是相等的,再输出该数值3。此处注意有if判断。里面有add方法,modCount是要加1的,现在modCount已经变成了4。

接着再进入35行开始迭代数组,执行hasNext方法,cursor为2,不等于size,为true,继续执行next方法,先判断modCount(也就是4)是否与expectedModCount(也就是3)相等,答案是不相等的,抛出异常。

整个流程结束。

归纳:

ArrayList是不能在遍历的时候,在对其进行修改操作的,包括增加和删除。也就是线程不安全的。如果在遍历的过程中有其他线程修改了lsit,则会抛出异常,这就是fast-fail(快速失败策略),这一策略在源码中的体现就是在next方法的时候,会调用checkForComodification方法,通过比较modCount和expectedModCount两者的是否相等,判断是否在遍历的时候,有进行修改操作,从而确定是否要抛出异常。那么在需要保证数组在遍历的时候不进行修改操作的时候,可以优先使用iterator来遍历。

猜你喜欢

转载自blog.csdn.net/qq_33774822/article/details/82757071