为何在for-each循环中调用remove方法未抛出ConcurrentModificationException?
ArrayList<String> collection = new ArrayList<String>(); collection.add("Apple"); collection.add("Banana"); collection.add("Cherry"); for (String element : collection) { if (element.equals("Banana")) { collection.remove(element); } }
问题
按照常规认知,在for-each循环中调用集合的remove方法应该抛出ConcurrentModificationException,但上述代码运行正常,并未出现预期的该异常,请问这是为什么?
解答
这是因为你移除的是集合的倒数第二个元素,刚好触发了ArrayList迭代器的实现细节,让异常没机会被抛出。
先拆解下for-each循环的本质:它底层依赖ArrayList的迭代器(Iterator)实现,迭代器内部维护了两个关键变量:
cursor:记录下一个要访问的元素索引expectedModCount:记录迭代器创建时集合的修改次数,用来和集合实际的modCount对比,一旦不一致就抛出异常
咱们一步步走代码执行流程:
- 初始状态:集合size=3,
cursor=0,expectedModCount=modCount=3 - 第一次循环:迭代器调用
next(),cursor从0变为1,返回元素"Apple",不触发移除,循环继续 - 第二次循环:迭代器调用
next(),cursor从1变为2,返回元素"Banana",此时调用collection.remove(),集合modCount变为4,size缩小为2 - 准备下一次循环:迭代器先调用
hasNext(),判断cursor < size——此时cursor=2,size=2,条件不成立,循环直接终止,根本没机会走到下一次next()里的checkForComodification()(检查修改次数的方法),自然不会抛异常
要是你把代码改成移除第一个元素"Apple",或者集合里有更多元素、移除的不是倒数第二个,循环就会继续执行下一次next(),这时就会检测到modCount和expectedModCount不一致,抛出ConcurrentModificationException。
注意:这只是ArrayList迭代器的特殊场景,绝对不能依赖这个行为写代码!正确的做法是用迭代器自身的remove()方法,或者用普通for循环从后往前遍历,避免触发fail-fast机制。
内容的提问来源于stack exchange,提问作者Shilpa Dolai
相关产品推荐
相关产品推荐

