181

注:私はIterator#remove()方法を知っています。

次のコード サンプルでは、​​メソッド内ではなくList.removeinメソッドで がmainスローされる理由がわかりません。ConcurrentModificationExceptionremove

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}
4

10 に答える 10

266

理由は次のとおりです。Javadocにあるように:

このクラスの iterator および listIterator メソッドによって返される反復子はフェイルファストです。反復子の作成後にリストが構造的に変更された場合、反復子自体の remove または add メソッド以外の方法で、反復子は ConcurrentModificationException をスローします。

このチェックはnext()、反復子のメソッドで行われます (スタック トレースで確認できます)。next()ただし、true が配信された場合にのみメソッドに到達しhasNext()ます。これは、境界が満たされているかどうかを確認するために for each によって呼び出されるものです。remove メソッドで、hasNext()別の要素を返す必要があるかどうかを確認すると、2 つの要素が返されたことがわかります。1 つの要素が削除された後、リストには 2 つの要素しか含まれていません。これで、すべてが桃色になり、反復が完了しました。next()これは呼び出されないメソッドで行われるため、同時変更のチェックは行われません。

次に、2 番目のループに進みます。2 番目の数値を削除した後、hasNext メソッドはさらに値を返すことができるかどうかを再度確認します。すでに 2 つの値が返されていますが、リストには 1 つしか含まれていません。しかし、ここのコードは次のとおりです。

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

1 != 2 なので、next()メソッドに進みます。このメソッドは、誰かがリストをいじっていることに気づき、例外を発生させます。

質問が解決することを願っています。

概要

List.remove()ConcurrentModificationExceptionリストから最後から 2 番目の要素を削除すると、スローされません。

于 2011-11-18T22:11:22.387 に答える
42

Collection該当する場合、(コレクション自体ではない)のコピーから何かを削除することを処理する1つの方法。Clone元のコレクションを介してコピーを作成しますConstructor

この例外は、そのような変更が許可されていない場合に、オブジェクトの同時変更を検出したメソッドによってスローされる場合があります。

finalあなたの特定のケースでは、最初に、宣言を過ぎたリストを変更するつもりであることを考えると、私は行く方法ではないと思います

private static final List<Integer> integerList;

また、元のリストの代わりにコピーを変更することも検討してください。

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}
于 2011-11-18T21:45:16.750 に答える
8

このスニペットは常にConcurrentModificationException をスローします。

ルールは、「反復子を使用して繰り返し処理している間は、変更 (リストから要素を追加または削除) することはできません (これは、for-each ループを使用するときに発生します)」です。

JavaDoc:

このクラスの iterator および listIterator メソッドによって返される反復子は、フェイルファストです。反復子の作成後に、反復子自体の remove メソッドまたは add メソッド以外の方法でリストが構造的に変更された場合、反復子は ConcurrentModificationException をスローします。

したがって、リスト(または一般的なコレクション)を変更する場合は、イテレータを使用します。これは、変更を認識し、適切に処理されるためです。

お役に立てれば。

于 2011-11-18T21:41:49.543 に答える
1

コピー オン ライト コレクションを使用すると機能します。ただし、list.iterator() を使用すると、別のスレッドがコレクションを変更した場合でも、返された Iterator は、(以下のように) list.iterator() が呼び出されたときと同じように、常に要素のコレクションを参照します。コピー オン ライト ベースの Iterator または ListIterator (追加、設定、または削除など) で呼び出される変更メソッドは、UnsupportedOperationException をスローします。

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new CopyOnWriteArrayList<>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}
于 2015-02-24T20:05:12.723 に答える
0

これはJava 1.6で問題なく動作します

~ % javac RemoveListElementDemo.java
~ % java RemoveListElementDemo
~ % cat RemoveListElementDemo.java

import java.util.*;
public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

〜%

于 2012-07-12T13:26:35.557 に答える