为何ListIterator.add不抛出ConcurrentModificationException而list.add会抛出?
这个问题问到点子上了!其实核心原因在于ArrayList的fail-fast机制以及两种修改方式对迭代器状态的同步处理不同,我给你一步步讲清楚:
首先先把你的代码贴出来方便对照:
List<String> list = new ArrayList<String>(); list.add("hi"); list.add("whats up"); list.add("how are you"); list.add("bye"); ListIterator<String> i = list.listIterator(); while (i.hasNext()) { // 第7行:i.add("元素") → 不会报错 // 第8行:list.add("元素") → 抛出ConcurrentModificationException }
先搞懂ConcurrentModificationException为什么会出现
ArrayList内部维护了一个叫modCount的变量,用来记录集合被修改的次数(比如add、remove、clear这些操作都会让它自增)。当你通过list.listIterator()创建迭代器时,迭代器会把当前的modCount值存到自己的expectedModCount变量里。
在迭代过程中,每次调用hasNext()或者next()的时候,迭代器都会检查modCount和expectedModCount是否相等:如果不相等,就说明集合在迭代器不知情的情况下被修改了,这时候就会抛出ConcurrentModificationException——这就是所谓的fail-fast快速失败,用来提醒你迭代过程中不要随便修改集合。
直接调用list.add("元素")的问题
当你在循环里直接调用list.add()时,只会修改ArrayList的modCount(让它+1),但迭代器里的expectedModCount完全没变化。下一次循环执行i.hasNext()的时候,迭代器发现modCount != expectedModCount,立刻就抛出异常了。
ListIterator.add("元素")为什么安全?
ArrayList的ListIterator其实是它内部的ListItr类实现的,这个类重写了add方法,做了两个关键操作:
- 调用ArrayList的底层添加逻辑把元素加进去(这时候
modCount会自增) - 同步迭代器的
expectedModCount,让它等于最新的modCount - 调整迭代器的
cursor(当前迭代位置),保证后续迭代能正确遍历新添加的元素
简单说,ListIterator的add方法是迭代器自己管理的修改操作,它知道集合被修改了,所以会同步自己的状态,让后续的迭代检查能通过,自然就不会抛出异常。
总结一下区别
- 直接调用
list.add():属于迭代器外部的修改,迭代器完全不知情,触发fail-fast检测,抛异常 - 调用
ListIterator.add():属于迭代器内部的安全修改,会同步迭代器和集合的状态,避免触发fail-fast检测
内容的提问来源于stack exchange,提问作者neha

