一尘不染

为什么此代码不引发ConcurrentModificationException?

java

为什么这段代码不抛出ConcurrentModificationException?它Collection在不使用Iterator.remove()方法的情况下修改了一段时间,这是唯一安全的删除方法

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String string : strings)
    if ("B".equals(string))
        strings.remove("B");
System.out.println(strings);

如果将替换为ArrayList,则会得到相同的结果LinkedList。但是,如果我将列表更改为("A", "B", "C", "D)或只是("A", "B")得到了预期的异常。到底是怎么回事?我正在使用jdk1.8.0_25是否相关。

编辑

我找到了以下链接

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078

相关部分是

天真的解决方案是将协同修改检查添加到AbstractList的hasNext中,但这会使协同修改检查的成本加倍。事实证明,仅在最后一次迭代上进行测试就足够了,这几乎没有增加任何成本。换句话说,hasNext的当前实现:

    public boolean hasNext() {
        return nextIndex() < size;
    }

替换为以下实现:

    public boolean hasNext() {
        if (cursor != size())
            return true;
        checkForComodification();
        return false;
    }

由于Sun内部监管机构拒绝了此更改,因此不会进行此更改。正式裁定表明,更改“表明了对现有代码产生重大兼容性影响的潜力”。(“兼容性影响”是该修复程序有可能用ConcurrentModificationException替换无声行为)。


阅读 217

收藏
2020-09-08

共1个答案

一尘不染

通常,ConcurrentModificationException检测到 修改时引发s ,而不 引起
。如果修改后从未访问过迭代器,则不会引发异常。ConcurrentModificationException不幸的是,这一微小的细节使它对于检测数据结构的滥用非常不可靠,因为它们仅在损坏完成后才抛出。

这种情况不会抛出a,ConcurrentModificationException因为next()修改后不会在创建的迭代器上调用它。

For-each循环实际上是迭代器,因此您的代码实际上如下所示:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iter = strings.iterator();
while(iter.hasNext()){
    String string = iter.next();
    if ("B".equals(string))
        strings.remove("B");
}
System.out.println(strings);

考虑您的代码在您提供的列表上运行。迭代看起来像:

  1. hasNext() 返回true,进入循环,-> iter移至索引0,字符串=“ A”,未删除
  2. hasNext()返回true,继续循环-> iter移至索引1,删除字符串=“ B”。strings现在长度为2。
  3. hasNext() 返回false(iter当前位于最后一个索引,没有更多的索引可用),退出循环。

因此,ConcurrentModificationException当调用next()检测到进行了修改时抛出s时,此方案几乎避免了此类异常。

对于您的其他两个结果,我们确实有例外。对于"A", "B", "C", "D",删除“
B”之后,我们仍然处于循环中,并next()检测到ConcurrentModificationException,而对于"A", "B"我来说,它是某种ArrayIndexOutOfBounds被捕获并重新抛出为ConcurrentModificationException

2020-09-08