java - 为什么这段代码不抛出 ConcurrentModificationException?

标签 java

为什么这段代码不抛出 ConcurrentModificationException?它在迭代 Collection 时修改它,而不使用 Iterator.remove() 方法,即 the only safe way of removing .

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

相关部分是

The naive solution is to add comodification checks to hasNext in AbstractList, but this doubles the cost of comodification checking. It turns out that it is sufficient to do the test only on the last iteration, which adds virtually nothing to the cost. In other words, the current implementation of hasNext:

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

Is replaced by this implementation:

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

This change will not be made because a Sun-internal regulatory body rejected it. The formal ruling indicated that the change "has demonstrated the potential to have significant compatibility impact upon existing code." (The "compatibility impact" is that the fix has the potential to replace silent misbehavior with a ConcurrentModificationException.)

最佳答案

作为一般规则,检测到而不是引起修改时会抛出ConcurrentModificationException。如果你在修改后从不访问迭代器,它不会抛出异常。不幸的是,这个微小的细节使得 ConcurrentModificationExceptions 对于检测数据结构的误用相当不可靠,因为它们只有在损坏完成后才会被抛出。

此方案不会抛出 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,string = "A",未移除
  2. hasNext() 返回 true,继续循环 -> iter 移动到索引 1,string = "B",已删除。 strings 现在的长度为 2。
  3. hasNext() 返回 false(iter 当前在最后一个索引处,没有更多索引可走),退出循环。

因此,当对 next() 的调用检测到已进行修改时,会抛出 ConcurrentModificationExceptions,这种情况可以勉强避免此类异常。

对于您的其他两个结果,我们确实得到了异常(exception)。对于“A”、“B”、“C”、“D”,去掉“B”后我们还在循环中,next()检测到ConcurrentModificationException,而对于 "A", "B" 我想它是某种 ArrayIndexOutOfBounds 被捕获并重新抛出为 ConcurrentModificationException

关于java - 为什么这段代码不抛出 ConcurrentModificationException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29723458/

相关文章:

java - 使用 java api 对文档元数据执行范围搜索

java - 在android studio中显示每个for循环迭代输出

java - 更新 CardLayout 中的卡片

java - 有没有办法让 Maven 同时构建 1.5 和 1.6 字节码 jar?

java - Apache 弗林克 : Ordered timestamps with parallelism

java - 基本数组 - Java

java.util.logging.Logger 在 Linux 上不会写入日志文件

java - 如何在特定位置放置组件?

java - 在 volley 中发送 int 和 String 作为参数

java - 在带有自定义标签的android中使用 block 模板引擎