ArrayList子列表迭代器next操作中不可达的ConcurrentModificationException场景问询
咱们先把这段next方法的关键逻辑拆解开,一步步捋清楚:
@SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= SubList.this.size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[offset + (lastRet = i)]; }
首先得明确:SubList是原ArrayList的视图,它的size等于原列表的总元素数减去offset(也就是子列表在原列表中的起始索引)。比如原列表有10个元素,从索引3开始创建SubList,那SubList的size就是7。
接下来看两个异常判断的先后顺序和条件逻辑:
- 第一个判断
if (i >= SubList.this.size)会优先触发:当迭代器的cursor已经走到子列表末尾(遍历完所有子列表元素),直接抛出NoSuchElementException,根本不会进入第二个异常判断的分支。 - 当第一个判断不触发时,
i的取值范围是0 <= i < SubList.this.size,也就是i的最大值只能是SubList.size - 1。
咱们来算一下offset + i的最大值:offset + (SubList.size - 1) = offset + (原ArrayList.size() - offset - 1) = 原ArrayList.size() - 1
而ArrayList的内部实现有个核心规则:elementData数组的长度永远大于等于原列表的实际元素个数。因为每次添加元素时,如果数组容量不足,ArrayList会自动扩容,保证elementData的容量不会小于当前元素总数。
所以offset + i的最大值是原ArrayList.size() - 1,必然小于elementData.length(因为elementData.length >= 原ArrayList.size())。
这就意味着,第二个判断offset + i >= elementData.length的条件永远不可能成立,对应的ConcurrentModificationException分支是完全不可达的死代码。
补充一句:迭代器真正用来检测并发修改的逻辑是checkForComodification()方法——它会对比原列表的modCount和迭代器的expectedModCount,当原列表被结构性修改(比如add/remove)时,这个方法才会抛出ConcurrentModificationException,代码里的那个分支根本轮不到触发。
内容的提问来源于stack exchange,提问作者Farhan stands with Palestine

