私はこのようにそれを繰り返しながらArrayList
からいくつかの要素を削除しようとしています:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
もちろん、リストから項目を削除しようとすると同時にConcurrentModificationException
を反復しようとするとmyArrayList
が返されます。この問題を解決するための簡単な解決策はありますか?
他のみんなの答えに代わるものとして、私はいつもこのようなことをしました:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
これにより、イテレータを直接扱う必要がなくなりますが、別のリストが必要になります。私はいつもどんな理由でもこの経路を好んだ。
Java 8ユーザーはそれをすることができます:list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
SomeConditionが満たされるリストの要素を削除します
イテレータのremove()メソッドを使用する必要があります。これはforループが拡張されていないことを意味します。
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
ダメダメダメ!
単一の脅迫的なタスクでは、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キーワードのように)対応するマルチタスクアプローチを使用してください。
他の提案された解決策はうまくいくが、もしあなたが本当にその解決策をスレッドセーフにしたいのであれば、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);
もう1つの方法は、あなたのList
をarray
に変換し、それらを繰り返し、あなたのロジックに基づいて直接それらを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());
}
トラバース中にリストを変更したい場合は、Iterator
name__を使用する必要があります。そして、iterator.remove()
を使ってトラバース中に要素を削除することができます。
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();
}
}
基になるコレクションオブジェクトからオブジェクトを削除するために、反復子remove()関数が使用できます。しかしこの場合、リストから他のオブジェクトではなく同じオブジェクトを削除することができます。