【问题标题】:Java sibling removal throws ConcurrentModificationExceptionJava 同级删除引发 ConcurrentModificationException
【发布时间】:2013-05-19 07:10:34
【问题描述】:

我遇到以下问题:

给定:

public class A{
    Collection<B> elements = new ArrayList<B>();
}

public class B{
    Collection<B> linkedElements = new ArrayList<B>();
}

linkedElements 的所有元素也都属于元素。 我希望每次从元素集合中删除一个元素时,它的链接元素也会从该集合中删除。 我尝试将观察者附加到 Iterator.remove 操作并在那里触发从元素列表中删除linkedElements,但由于逻辑本身,我总是遇到 ConcurrentModificationException。

更新: 这是导致错误的代码:

public class A{
 Collection<B> elements = new ArrayList<B>(){
    public Iterator<B> iterator() {
        return new ProxyIterator(super.iterator());
    };

  private class ProxyIterator implements Iterator{

    Iterator it;

    Object lastObject;

    public ProxyIterator(Iterator proxied){
        it = proxied;
    }

    @Override
    public boolean hasNext() {
        return it.hasNext();
    }

    @Override
    public Object next() {
        return lastObject = it.next();
    }

    @Override
    public void remove() {
        it.remove()
        for (B linkedElement : ((B)lastObject).getlinkedElements()) {
            A.this.getElements().remove(linkedElement);
        }
    }

  }
}

使用此代码,只需调用 A.getElements().clear() 就会触发 ConcurrentModificationException... 并且没关系,因为我正在从元素列表中删除所有链接的元素,同时删除一个元素。这就是为什么我需要另一种方法。

【问题讨论】:

  • 请告诉我们导致此错误的代码。
  • 你如何将此观察者附加到迭代器?
  • 如果 linkedElementselements 列表的精确副本,我建议在 A 和 B 之间共享列表,而不是同步删除。

标签: java exception collections iterator concurrentmodification


【解决方案1】:

这是因为您在迭代数组时正在修改数组。来自 ArrayList 的 javadocs:

这个类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 并发修改异常。因此,面对并发 修改,迭代器快速而干净地失败,而不是 在不确定的时间冒着任意的、不确定的行为的风险 未来。

所以,一旦您在 remove 方法中执行 A.this.getElements().remove(linkedElement);,您现在只是通过迭代器 it 的“remove”方法以外的方式对列表进行了结构修改,这意味着迭代器将抛出 CME .

处理这个问题可能会很棘手。我可以临时想到几个选项,所有这些选项都有复杂性:

  • 切换到CopyOnWriteArrayList,因为它的迭代器是故障安全的。缺点是您的迭代器可能仍会显示以前删除的项目。 (另一方面,无论如何,您都必须应对这种风险,因为您也可能已经迭代过您要删除的任何内容的子节点。)如果这对您有用,那么这几乎肯定是最简单和最可靠的选项。
  • remove 中的for 循环之后,将it 替换为您手动前进到正确位置的新迭代器。如果您的列表允许重复项,则很难做到。
  • 重新实现ArrayList.Iterator;在您的 remove 方法中,您可以跟踪所做的更改并适当地更新您的迭代器。

最后,作为最后一个问题/警告 - 你希望这个遍历是递归的吗?现在,如果你有元素 Foo 链接到 Bar,Bar 链接到 Baz,从迭代器中删除 Foo 会导致 Bar 被删除,但是没有任何东西会删除 Baz。 (而如果您先删除 Bar,那么 Baz 将被删除。)一般而言,如果您想修改 Collection 的删除行为,最好使用 List.remove 而不是 Iterator.remove

【讨论】:

  • 很好的答案!。正如我在问题中所说,我知道从逻辑上讲,拥有 CME 是可以的。我的解决方案是您指出的第三个选项。我认为为这样一个常见的操作做一个棘手的解决方法有点愚蠢。 CopyOnWrite 集合具有非常昂贵的可变操作。关于您的最后一个问题:不,具有链接元素的项目不会包含在其他链接元素列表中(“父”元素不能是“子”)。
猜你喜欢
  • 1970-01-01
  • 2014-10-26
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 2014-06-19
  • 2019-01-23
  • 2022-01-24
  • 2019-03-19
相关资源
最近更新 更多