先附上阿里手册原文截图:
ok,故事开始了:
- 反例贴到IDE,在for后加上sysout(a), run一下,打印结果正确,没有任何问题;
- 把“1”改成“2”,看起没问题三,run一下
3.以checkForComodification关键字,百度一番,收获如下:
- checkForComodification()方法,主要就是检测modCount == expectedModCount ;
- expectedModCount 是在Itr中定义的:int expectedModCount = ArrayList.this.modCount;所以他的值是不可能会修改的,所以会变的就是modCount。
- ArrayList中无论add、remove、clear方法只要是涉及了改变ArrayList元素的个数的方法都会导致modCount的改变。
所以:
- 由于最开始add了2次,modCount =2;
- 进入foreach时,迭代器初始化了expectedModCount为2;
- for 里面remove了“2”,所以modCount+1,变为3,此时expectedModCount 不等于modCount了
似乎明白了,但是为什么remove“1”可以呢?关键时刻还是得靠跟踪源码:
迭代器有个游标cursor,移除2的时候,由于2是第二个元素,此时cursor为2。移除了“2”之后list的size变成了1,然后再进行for迭代的时候,先调用了hasnext判断是否到末尾,这货是这么判断的
。。。。。。。(此处省略一万字)
此时显然不相等,所以它以为还有next,所以调用了next方法
然后就在这里就悲剧的抛异常了..
也许没仔细看异常,还想当然的以为是remove的时候抛异常的,再看一下异常堆栈,没错,是remove成功后,再尝试迭代抛异常了...
OK,如果是remove“1”呢?
移除“1”时,cursor为1,而移除后,list中只有“2”,故size为1,所以hasnext返回了false,然后就没有然(异)后(常)了。。
也就是说,这个反例,只要在foreach中移除的不是倒数第二个元素就会报错,只有倒数第二个元素被移除的时候,移除后再循环时hasnext才会返回false;否则hasnext为true,进入next时报错。
for下标、iterator则可以,foreach不行。
所以,不要在foreach循环中做列表增删