【问题标题】:Why isn't ConcurrentModificationException being thrown here?为什么这里没有抛出 ConcurrentModificationException ?
【发布时间】:2018-01-05 07:46:33
【问题描述】:

看看这段小代码:

ArrayList al = new ArrayList();
al.add("AA");
al.add("AB");
al.add("AC");
Iterator it = al.iterator();
while(it.hasNext()){
String s = (String)it.next();
if(s.equals("AB")){
al.remove(1);
}
}

由于 ArrayList 具有快速失败迭代器,而且很明显,给定的 ArrayList 不是由固定大小的数组组成(这会使 remove() 方法不可用) ,上面的代码应该抛出了ConcurrentModificationException,但是是的,它没有。

另外,如果我在循环中插入一个打印语句(作为第一条语句),它表明循环没有第三次迭代并且它优雅地退出。

我知道这听起来太愚蠢了,但我能错误地想到的唯一原因是元素的删除发生在元素已被迭代器。但事实并非如此,因为 modificationCount 仍被删除修改,因此它必须抛出异常。

只是在做

while(it.hasNext()){
it.next();
al.remove(1);
}

虽然会抛出 ConcurrentModificationException。

有什么见解吗?

【问题讨论】:

    标签: java iterator concurrentmodification fail-fast


    【解决方案1】:

    仅在迭代器的 next() 调用期间检查并发修改,而不是在其 hasNext() 调用期间进行,如 Bubletan's answer 中所述。

    java documentation for ArrayList 明确指出,

    快速失败的迭代器在 a 上抛出 ConcurrentModificationException 尽力而为的基础。因此,编写程序是错误的 这取决于此异常的正确性:快速失败 迭代器的行为应该只用于检测错误。

    因此,在迭代时修改集合是一种错误的编程习惯。

    【讨论】:

    • 在我执行的所有执行过程中,一个非常简单的流程中的更改仍然未被检测到,尽力而为的基础是否如此糟糕?我曾相信还有其他类型的信号处理线程负责监听修改。从您告诉我的内容来看,如果我在使用迭代器遍历时尝试删除最后一个元素,则不会因为没有更多 next() 调用而引发异常,不是吗?
    • Bubletan 的回答说明了一切。这真的不应该被称为尽力而为:D 谢谢!
    【解决方案2】:

    这是因为hasNext() 方法没有检查modCount

    public boolean hasNext() {
        return cursor != size;
    }
    

    所以,在调用remove(1) 之后,列表的大小将和光标一样为2,hasNext() 将返回false。 next() 方法永远不会被调用,modCount 永远不会被检查。

    如果您在迭代之前将第四个元素添加到列表中,您将获得与第二个示例类似的异常。

    【讨论】:

    • 喜欢准确、简短和有益健康的答案!非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 2014-09-11
    • 2011-08-29
    • 1970-01-01
    • 2021-09-03
    • 2015-12-24
    • 1970-01-01
    相关资源
    最近更新 更多