この例でjava.util.ConcurrentModificationExceptionが返されないのはなぜですか?


176

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

なぜ次のコードサンプルでは、私は理解していないList.removemainメソッドがスローしConcurrentModificationExceptionますが、ないremove方法。

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

3
リストを反復しながら要素をリストから削除する唯一の安全な方法は、を使用することIterator#remove()です。なぜあなたはそれをこのようにしているのですか?
マットボール

@MattBall:私はここで理由が何であるかを確認しようとしていました。なぜなら、それは両方のメソッドで同じ「強化されたforループ」ですが、1つはスローしConcurrentModificationException、もう1つはスローしません。
Bhesh Gurung、

削除する要素に違いがあります。メソッドでは、「中間要素」を削除します。メインでは最後を削除します。数値を交換すると、メソッドで例外が発生します。それがなぜかはまだわかりません。
ベンファンゴンペル

同様の問題がありました。ループでアイテムを削除した後、存在しない位置もループで繰り返したところです。return;ループにa を追加するだけでこれを修正しました。
frank17

java8 Androidでは、最後の要素以外の要素を削除すると、ConcurrentModificationExceptionが呼び出されます。したがって、あなたの場合、削除関数は前に観察したのとは反対の例外を受け取ります。
gonglong

回答:


262

理由は次のとおりです。Javadocで述べられているように、

このクラスのiteratorメソッドとlistIteratorメソッドによって返されるイテレータはフェイルファストです。イテレータが作成された後、リストが構造的にいつでも変更された場合、イテレータ自身の削除または追加メソッド以外の方法で、イテレータはConcurrentModificationExceptionをスローします。

このチェックはnext()イテレータのメソッドで行われます(スタックトレースで確認できます)。ただし、trueが配信された場合にnext()のみメソッドに到達し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番目の最後の要素を削除してもスローされません。


5
@pushy:質問が実際に尋ねていることに答えるように見える答えのみで、説明は良いです。この回答と+1を受け入れます。ありがとう。
Bhesh Gurung

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

14

アイテムを削除する場合、転送/反復メソッドは機能しません。エラーなしで要素を削除できますが、削除されたアイテムにアクセスしようとするとランタイムエラーが発生します。イテレータを使用することはできません。プッシュが示すように、ConcurrentModificationExceptionが発生するためです。代わりに、通常のforループを使用しますが、逆方向に進みます。

List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

int size= integerList.size();

//Item to remove
Integer remove = Integer.valueOf(3);

解決策:

リスト要素を削除する場合は、配列を逆の順序でトラバースします。リストを逆方向に移動するだけで、削除されたアイテムにアクセスする必要がなくなり、例外が削除されます。

//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
    if (integerList.get(i).equals(remove) ) {
        integerList.remove(i);
    }
}

素晴らしいアイデア!
dobrivoje

7

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

ルールは、「イテレーターを使用して反復している間(for-eachループを使用すると発生します)を変更(リストに要素を追加または削除)することはできません」です。

JavaDocs:

このクラスのiteratorメソッドとlistIteratorメソッドによって返されるイテレータはフェイルファストです。イテレータが作成された後、リストが構造的にいつでも変更された場合、イテレータ自身の削除または追加メソッド以外の方法で、イテレータはConcurrentModificationExceptionをスローします。

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

お役に立てれば。


3
OPは、ループの1つが例外をスローしないことと、質問がその原因であると明確に述べています。
madth3

「尋問」とはどういう意味ですか?
ブーシャン

4

同じ問題がありましたが、en要素を反復リストに追加した場合です。このように作りました

public static void remove(Integer remove) {
    for(int i=0; i<integerList.size(); i++) {
        //here is maybe fine to deal with integerList.get(i)==null
        if(integerList.get(i).equals(remove)) {                
            integerList.remove(i);
        }
    }
}

リストにイテレータを作成しないため、すべてがうまくいきます。「手動」でイテレータを繰り返します。そしてi < integerList.size()、あなたが何かをリストのサイズに削除/追加すると、リストのデクリメント/インクリメント..

それが役に立てば幸い、私にとってそれは解決策でした。


本当じゃない !証拠:このスニペットを実行して結果を確認します:public static void main(String ... args){List <String> listOfBooks = new ArrayList <>(); listOfBooks.add( "Code Complete"); listOfBooks.add( "Code 22"); listOfBooks.add( "22 Effective"); listOfBooks.add( "Netbeans 33"); System.err.println( "削除前:" + listOfBooks); for(int index = 0; index <listOfBooks.size(); index ++){if(listOfBooks.get(index).contains( "22")){listOfBooks.remove(index); }} System.err.println( "削除後:" + listOfBooks); }
dobrivoje

1

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

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

〜%


タイプミスについて申し訳ありません。これはJava 1.6で正常に実行されます
バトサイ

うーん...別の実装があるかもしれません。しかし、仕様によると、それはそれを行うことになっています、IMO。@Pushyの答えを見てください。
ベシュグルン

残念ながら、idはJava 1.8にはありません
dobrivoje

0

私の場合は次のようにしました:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());

0

イテレーターfor eachfor loopに変更して解決します。

そしてその理由は:

このクラスのiteratorメソッドとlistIteratorメソッドによって返されるイテレータはフェイルファストです。イテレータが作成された後、リストが構造的にいつでも変更された場合、イテレータ自身の削除または追加メソッド以外の方法で、イテレータはConcurrentModificationExceptionをスローします。

-参照されるJavaドキュメント。


-1

コードマンを確認してください...

メインメソッドでは、存在しない4番目の要素を削除しようとしているため、エラーが発生しています。remove()メソッドでは、3番目の要素を削除しようとしているため、エラーは発生しません。


あなたは間違っています:数23リストのインデックスではなく要素です。両方の削除ロジックequalsは、要素のインデックスではなく、リスト要素に対してチェックします。さらに、それがインデックス関連である場合、それはでIndexOutOfBoundsExceptionはなく、ConcurrentModificationExceptionです。
Malte Hartwig、2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.