0

次の問題があります。

与えられた:

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

  }
}

このコードでは、 aA.getElements().clear()を呼び出すだけで a ConcurrentModificationException... が発生します。1 つの要素を削除しながら、要素リストからすべてのリンクされた要素を削除しているためです。だからこそ、別のアプローチが必要なのです。

4

1 に答える 1

1

これは、反復処理中に配列を変更しているためです。ArrayList の javadoc から:

このクラスの iterator および listIterator メソッドによって返される反復子はフェイルファストです。反復子の作成後にリストが構造的に変更された場合、反復子自体の remove メソッドまたは add メソッド以外の方法で、反復子は ConcurrentModificationException をスローします。したがって、同時変更に直面した場合、反復子は、将来の不確定な時点で恣意的で非決定論的な動作を危険にさらすのではなく、迅速かつ明確に失敗します。

したがって、remove メソッドで行うと、 iteratorの「remove」メソッドA.this.getElements().remove(linkedElement);以外の手段でリストを構造的に変更したことになります。つまり、iterator は CME をスローします。it

これに対処するのはおそらく難しいでしょう。いくつかのオプションをすぐに思いつくことができますが、それらにはすべて複雑な問題があります。

  • CopyOnWriteArrayListイテレータはフェイルセーフであるため、 に切り替えます。欠点は、以前に削除されたアイテムがイテレータに表示される可能性があることです。(一方で、削除しようとしているものの子を既に反復処理している可能性があるため、とにかくそのようなリスクに対処する必要があります。) これがうまくいく場合、これはほぼ確実に最も簡単で最も信頼できる方法です。オプション。
  • の for ループに続いて、手動で正しい場所に進む新しい Iteratorremoveに置き換えます。itリストが重複するアイテムを許可している場合、実行するのは困難です。
  • ArrayList.Iterator を再実装します。メソッドでは、remove行っている変更を追跡し、イテレータを適切に更新できます。

最後に、最後の質問/警告として、このトラバーサルを再帰的にしますか? 現在、要素 Foo が Bar にリンクされ、Bar が Baz にリンクされている場合、反復子から Foo を削除すると Bar が削除されますが、Baz を削除するものは何もありません。(一方、最初に Bar を削除すると、Baz が削除されます。) 一般に、 の削除動作を変更する場合CollectionList.removeIterator.remove.

于 2013-05-23T21:47:40.617 に答える