List快速失败机制:为什么这段代码的执行结果会不同

image.png

if("2".equals(item)) 替换成if("1".equals(item))执行结果不同

2的时候会抛快速失败异常,1的时候没有异常

阅读 1.7k
2 个回答

其实呢,结论就是仅仅是巧合而已。。。对,就是只是巧合,这跟快速失败关系不大。当然最终报错是快速失败,但是导致一个报错一个不报错,跟快速失败实现关系不大

题主的给的代码编译后就是如下样子
image.png

调用顺序是

  1. ArrayList.iterator(),这里迭代器类是内部私有类ArrayList.Itr
  2. var2.hasNext(),也就是ArrayList.Itr.hasNext()
  3. var2.next(),也就是ArrayList.Itr.next()

然后debug这几个方法就可以简单看明白,假设现在是"2".equals(item)的情况,也就是报错的情况

第一个方法iterator()自不用说,主要是后两个方法

ArrayList.Itr.hasNext()的实现,主要是判断当前循环下标cursorArrayList的大小size来决定是否还要循环的
image.png

第一次进入hasNext()方法时,cursor为0,size为2,两不相等,所以循环继续

ArrayList.Itr.next()的第一步就有了快速失败检查checkForComodification(),这自不用说,没问题
image.png

然后判断"2".equals(item)没进入,再次走入hasNext()方法,cursor为1,size为2,两不相等,所以循环继续

这一次执行完next()方法后,判断"2".equals(item)进入了,导致size变为1

第三次走入hasNext()方法,cursor为2,size为1,本来已经循环两次,要停止了,由于hasNext()判断模式+size变化,所以循环继续,这一次当然最后就是快速失败了。

所以模仿上面的逻辑走向,判断条件变为"1".equals(item)时为啥不会报错,就是因为第一次进入循环,判断条件"1".equals(item)成立导致size变为1,第二次进入hasNext()方法时,此时恰好cursor为1,size也为1,两个相等,所以循环就停止了,也不会进入next()方法,也就不会报快速失败的错了。

这也是为啥呼吸    ༽提到要直接使用迭代器Iterator来操作,因为for循环的语法,最终会转换成Iterator的模式

但是你删除使用的方法却是ArrayListremove,而不是Iteratorremove
image.png

因此"2".equals(item)时会导致快速失败,而Iteratorremove是有对快速失败的变量expectedModCount有做修正的,因此不会报错
image.png

记住 , list的remove操作,不要直接操作,转换成Iterator再进行操作

List<String> list = new ArrayList<>(16);
list.add("1");
list.add("2");
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
    if (iter.next().equals("2")) {
        iter.remove();  
    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题