为什么增强for循环删除ArrayList元素“2”不抛出ConcurrentModificationException
现象原因分析
首先明确两个底层逻辑:
- Java的增强for循环遍历集合本质是语法糖,底层依赖集合的
Iterator迭代器实现遍历 - ArrayList的迭代器内部维护3个核心变量:
modCount:集合本身的修改次数,每次add/remove操作都会自增expectedModCount:迭代器初始化时拷贝的modCount值,遍历过程中如果和modCount不一致就会触发ConcurrentModificationExceptioncursor:下一个待遍历元素的索引,初始为0,每调用一次next()方法就自增1,hasNext()方法的判断逻辑为return cursor != size;
对应代码的执行过程拆解
集合初始化后共有3个元素,size=3,元素按顺序为"1"、"2"、"3",对应索引为0、1、2:
- 迭代器初始化:
expectedModCount = modCount = 3,cursor=0 - 第一次遍历:
hasNext()判断0 != 3返回true,调用next()返回索引0的"1",cursor变为1,校验expectedModCount和modCount一致,无异常,不满足删除条件,继续遍历 - 第二次遍历:
hasNext()判断1 !=3返回true,调用next()返回索引1的"2",cursor变为2,校验一致,无异常。匹配删除条件后调用list.remove("2"),此时集合size变为2,modCount自增为4,迭代器的expectedModCount仍为3,cursor保持为2 - 第三次遍历前先调用
hasNext():判断cursor=2是否等于当前集合size=2,结果为true,直接返回false,循环终止,不会调用next()方法,也就不会触发expectedModCount和modCount的一致性校验,因此不会抛出异常。
注意:这是非常偶然的边界情况,只有删除倒数第二个元素时才会触发,删除其他位置的元素都会因为后续还会调用
next()触发校验抛出异常,千万不要把这种巧合当成规范写法,遍历中删除元素一定要使用迭代器自带的remove()方法,或者使用Stream的filter方法生成新集合。
内容的提问来源于stack exchange,提问作者Daiqing Yang
相关产品推荐
相关产品推荐

