web-dev-qa-db-ja.com

それを反復している間 `ArrayList`から要素を削除している間「ConcurrentModificationException」を避ける方法は?

私はこのようにそれを繰り返しながらArrayListからいくつかの要素を削除しようとしています:

for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

もちろん、リストから項目を削除しようとすると同時にConcurrentModificationExceptionを反復しようとするとmyArrayListが返されます。この問題を解決するための簡単な解決策はありますか?

309

Iterator を使用して remove() を呼び出します。

Iterator<String> iter = myArrayList.iterator();

while (iter.hasNext()) {
    String str = iter.next();

    if (someCondition)
        iter.remove();
}
516
arshajii

他のみんなの答えに代わるものとして、私はいつもこのようなことをしました:

List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

これにより、イテレータを直接扱う必要がなくなりますが、別のリストが必要になります。私はいつもどんな理由でもこの経路を好んだ。

164
Kevin DiTraglia

Java 8ユーザーはそれをすることができます:list.removeIf(...)

    List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    list.removeIf(e -> (someCondition));

SomeConditionが満たされるリストの要素を削除します

79

イテレータのremove()メソッドを使用する必要があります。これはforループが拡張されていないことを意味します。

for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}
56
Eric Stein

ダメダメダメ!

単一の脅迫的なタスクでは、Iterator、さらにCopyOnWriteArrayListを使用する必要はありません(パフォーマンスが低下するため)。

解決法はもっと簡単です: for-each loop の代わりに標準のforループを使うようにしてください。

Javaの著作権所有者(数年前のSun、現在はOracle)によると、 for - each-loopガイド 、それはコレクションを見ていくためにイテレータを使い、コードを見栄えよくするためにそれを隠している。しかし、残念なことに、私たちが知ることができるように、それは利益より多くの問題を生み出しました、さもなければこのトピックは起こらないでしょう。

たとえば、次のコードでは、変更されたArrayListに次の繰り返しを入力するとJava.util.ConcurrentModificationExceptionが発生します。

        // process collection
        for (SomeClass currElement: testList) {

            SomeClass founDuplicate = findDuplicates(currElement);
            if (founDuplicate != null) {
                uniqueTestList.add(founDuplicate);
                testList.remove(testList.indexOf(currElement));
            }
        }

しかし、次のコードは問題なく動作します。

    // process collection
    for (int i = 0; i < testList.size(); i++) {
        SomeClass currElement = testList.get(i);

        SomeClass founDuplicate = findDuplicates(currElement);
        if (founDuplicate != null) {
            uniqueTestList.add(founDuplicate);
            testList.remove(testList.indexOf(currElement));
            i--; //to avoid skipping of shifted element
        }
    }

そのため、コレクションを反復処理するためにインデックス作成アプローチを使用し、for-eachループは避けてください。これらは等価ではありません。 for-eachループはコレクションの変更をチェックし、ConcurrentModificationException例外をスローする内部イテレータを使用します。これを確認するために、私が投稿した最初の例を使用するときには、印刷されたスタックトレースをよく見てください。

Exception in thread "main" Java.util.ConcurrentModificationException
    at Java.util.AbstractList$Itr.checkForComodification(AbstractList.Java:372)
    at Java.util.AbstractList$Itr.next(AbstractList.Java:343)
    at TestFail.main(TestFail.Java:43)

マルチスレッドの場合は、(synchronizedキーワードのように)対応するマルチタスクアプローチを使用してください。

31
Dima Naychuk

他の提案された解決策はうまくいくが、もしあなたが本当にその解決策をスレッドセーフにしたいのであれば、ArrayListを CopyOnWriteArrayListに置き換えるべきです

    //List<String> s = new ArrayList<>(); //Will throw exception
    List<String> s = new CopyOnWriteArrayList<>();
    s.add("B");
    Iterator<String> it = s.iterator();
    s.add("A");

    //Below removes only "B" from List
    while (it.hasNext()) {
        s.remove(it.next());
    }
    System.out.println(s);
8
Prashant Bhate

もう1つの方法は、あなたのListarrayに変換し、それらを繰り返し、あなたのロジックに基づいて直接それらをListから削除することです。

List<String> myList = new ArrayList<String>(); // You can use either list or set

myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");

Object[] obj = myList.toArray();

for(Object o:obj)  {
    if(condition)
        myList.remove(o.toString());
}
7
CarlJohn

トラバース中にリストを変更したい場合は、Iteratorname__を使用する必要があります。そして、iterator.remove()を使ってトラバース中に要素を削除することができます。

6
Juned Ahsan
List myArrayList  = Collections.synchronizedList(new ArrayList());

//add your elements  
 myArrayList.add();
 myArrayList.add();
 myArrayList.add();

synchronized(myArrayList) {
    Iterator i = myArrayList.iterator(); 
     while (i.hasNext()){
         Object  object = i.next();
     }
 }
6
Prabhakaran

基になるコレクションオブジェクトからオブジェクトを削除するために、反復子remove()関数が使用できます。しかしこの場合、リストから他のオブジェクトではなく同じオブジェクトを削除することができます。

ここから

1
gilo