为什么这段代码不会抛出ConcurrentModificationException? [英] Why doesn't this code throw a ConcurrentModificationException?

查看:94
本文介绍了为什么这段代码不会抛出ConcurrentModificationException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么这段代码不会抛出 ConcurrentModificationException ?它在迭代它时修改 Collection ,而不使用 Iterator.remove()方法,这意味着唯一安全的删除方式

Why doesn't this code throw a ConcurrentModificationException? It modifies a Collection while iterating through it, without using the Iterator.remove() method, which is meant to be 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 如果相关的话。

I get the same result if I replace the ArrayList with a LinkedList. However if I change the list to ("A", "B", "C", "D) or just ("A", "B") I get the exception as expected. What is going on? I am using jdk1.8.0_25 if that is relevant.

编辑

我找到了以下链接

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

相关部分


天真的解决方案是在AbstractList中为hasNext添加编码检查,但这会使编纂检查的成本增加一倍。
事实证明只在最后的
迭代上进行测试就足够了,这几乎没有增加成本。换句话说,
当前实现的hasNext:

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;
    }

替换为此实现:

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

由于Sun内部监管机构拒绝了此更改,因此不会进行此更改。正式裁决表明,该变更有b $ b b表明可能会对现有代码造成重大兼容性影响
。 (兼容性影响是修复具有
可能用
ConcurrentModificationException替换无声错误行为。)

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 ,而不是引起。如果您在修改后从未访问过迭代器,则不会抛出异常。遗憾的是,这个细节使得 ConcurrentModificationException 对于检测数据结构的滥用是相当不可靠的,因为它们仅在损坏完成后被抛出。

As a general rule, ConcurrentModificationExceptions are thrown when the modification is detected, not caused. If you never access the iterator after the modification, it won't throw an exception. This minute detail makes ConcurrentModificationExceptions rather unreliable for detecting misuse of data structures, unfortunately, as they only are thrown after the damage has been done.

此方案不会抛出 ConcurrentModificationException ,因为 next()未被调用在修改后创建的迭代器上。

This scenario doesn't throw a ConcurrentModificationException because next() doesn't get called on the created iterator after the modification.

For-each循环实际上是迭代器,所以你的代码实际上是这样的:

For-each loops are really iterators, so your code actually looks like this:

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);

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

Consider your code running on the list you provided. The iterations look like:


  1. hasNext()返回true,输入循环, - > iter移动到索引0,字符串=A,未移除

  2. hasNext()返回true,继续循环 - > iter移动到索引1,字符串=B,删除。 字符串现在长度为2.

  3. hasNext()返回false(iter)目前在最后一个索引,没有更多的索引去),退出循环。

  1. hasNext() returns true, enter loop, -> iter moves to index 0, string = "A", not removed
  2. hasNext() returns true, continue loop -> iter moves to index 1, string = "B", removed. strings now has length 2.
  3. hasNext() returns false (iter is currently at the last index, no more indices to go), exit loop.

因此,作为 ConcurrentModificationException当 next()的调用检测到已经进行了修改时,抛出 s,这种情况可以避免这种异常。

Thus, as ConcurrentModificationExceptions are thrown when a call to next() detects a that a modification has been made, this scenario narrowly avoids such an exception.

对于您的其他两个结果,我们会得到例外情况。对于A,B,C,D,删除B后我们仍然在循环中,然后 ()检测到 ConcurrentModificationException ,而对于A,B我' d想象它是某种ArrayIndexOutOfBounds被捕获并重新抛出为 ConcurrentModificationException

For your other two results, we do get exceptions. For "A", "B", "C", "D", after removing "B" we are still in the loop, and next() detects the ConcurrentModificationException, whereas for "A", "B" I'd imagine it's some kind of ArrayIndexOutOfBounds that's being caught and re-thrown as a ConcurrentModificationException

这篇关于为什么这段代码不会抛出ConcurrentModificationException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆