改造点三:一步一输出
这一个改造点,我就不进行详细说明了,授人以鱼不如授人以渔,前面两个改造点你如果会了,那你也能继续改造,得到下面的程序,并搞出一步一输出日志:
上面这图,就是我们最后需要分析的程序和日志了。
如果你对于得到上面的输出还是有点困难的话,你可以在文末找到我的git地址,我把程序都上传到了git上。
真相已经摆在眼前了
其实你想一想,还用分析吗?经过上面的三个"骚"操作后,真相已经摆在眼前了。
以这位读者的问题举例.
第一个问题:为什么判断"why技术"并remove的时候循环三次?
你品一品这个输出,这就是真相呀!为什么会循环三次,一目了然了啊!
【第1次循环】cursor=0,size=2,判定结果:true
【第1次循环】var3.next方法被调用cursor进行加一操作
【第2次循环】cursor=1,size=2,判定结果:true
【第2次循环】var3.next方法被调用cursor进行加一操作
【第2次循环】list.remove方法被调用size进行减一操作
【第3次循环】cursor=2,size=1,判定结果:true
再回答另外一个问题:为什么注释掉remove只循环两次?
你再品一品这个输出:
第三个问题:为什么判断"公众号"并remove的时候只循环一次?
继续品这个输出:
致命一问,灵魂一击
对于之前列举的其他问题,你有没有发现其实有很多共同的地方,但是我故意扰乱了你的判断,你仔细读这几个问题:
当集合大小等于2时,为什么删除第一个元素(公众号)可以正常执行?
当集合大小大于2时,删除第一个元素(公众号)也抛出了异常?
为什么删除倒数第二个元素可以正常执行?
上面的三个问题其实是在说一个问题,你发现了吗?
当集合大小等于2时第一个元素(公众号),是不是就是倒数第二个元素?!
恍然大悟有没有?
再看一个示例:
敲黑板,数学推理来了:
在单线程的情况下,只要你的ArrayList集合大小大于等于2(假设大小为n,即size=n),你删除倒数第二个元素的时候,cursor从0进行了n-1次的加一操作,size(即n)进行了一次减1的操作,所以n-1=n-1,即cursor=size。
因为判断条件返回为fales,虽然你的modCount变化了。但是不会进入下次循环,就不会触发modCount和expectedModCount的检查,也就不会抛出ConcurrentModifyException.