首先,虽然这篇博文的名称是foreach循环ArrayList时不能使用ArrayList的增删方法之源码简析,但是源码简析的主要部分其实在另一篇博文(foreach迭代ArrayList时,真的不能删除元素吗?,以下简称原博文)。
本文其实是基于原博文做了点小小的扩展。所以在继续往下阅读之前,请先阅读原博文,否则可能会不太能理解我下面在说些什么!
(为什么不再自己从头分析一遍相关源码呢?一是因为我比较懒,但是最主要还是因为我觉得原博文已经分析得很好很清晰了。)
——————————————————— 手动分割线 ————————————————————
既然看到了这里,那么我就默认你已经阅读过原博文了,接下来就先解答原博文中作者在文末提出的问题。
为了方便阅读,这里再把问题贴出来:
其实吧,我觉得在作者提的问题中已经给出了答案:执行完这次打印,进入下一次迭代时,又产生了checkForComodification异常。
为什么又产生了checkForComodification异常?就是因为又进入了下一次迭代,又先后执行了hasNext()方法和next()方法,在next()方法中做checkForComodification()校验时,因为modCount != expectedModCount,所以抛出异常。
那么,为什么循环到倒数第二个元素时,remove一个元素不会抛出异常,而remove两个元素却抛出异常呢?
我们来反推一下,抛出ConcurrentModificationException异常是在checkForComodification()校验中,而checkForComodification()校验是在next()方法中调用的,而想要调用next()方法,就需要hasNext()方法返回true,即迭代器认为还有下一个元素。
所以,我们已经可以猜到了,循环到倒数第二个元素时remove一个元素,接下来hasNext()方法返回了false,即判断已经没有下一个元素了,所以不会再调用next()方法,也就不会再做checkForComodification()校验,所以不会抛出异常;而remove两个元素后执行hasNext()方法返回了true,即判断还有下一个元素,所以继续调用next()方法,继续调用checkForComodification()校验,进而抛出异常。
上面是我们通过反推后得出的猜想,那么到底是不是这个原因呢?接下来就看一下hasNext()方法的具体实现。
可以看到hasNext()方法的实现很简单,仅仅是判断 cursor 和 size 是否相等,不相等即认为还有下一个元素,只有相等时才认为没有下一个元素了。
其中,size大家都懂,表示的是ArrayList包含的元素的实际个数。cursor表示的是 index of next element to return,我们可以简单理解为迭代器指向的下一个元素的下标(简单理解)。
理解了 cursor 和 size 代表的含义后,我们来举个栗子:
List<String> list = new ArrayList<>()</