@Test
public void testListCur(){
List<String> li=new ArrayList<String>();
for(int i=0;i<10;i++){
li.add("str"+i);
}
for(String st:li){
if(st.equalsIgnoreCase("str3"))
li.remove("str3");
}
System.out.println(li);
}
このコードを実行すると、ConcurrentModificationExceptionがスローされます。
リストから指定された要素を削除すると、リストはそのサイズが変更されたことを知らないように見えます。
これがコレクションと要素の削除に関する一般的な問題かどうか疑問に思っていますか?
これが Iterator.remove() メソッドの背後にある目的であり、反復中にコレクションから要素を削除できるようにするためだと思います。
例えば:
Iterator<String> iter = li.iterator();
while(iter.hasNext()){
if(iter.next().equalsIgnoreCase("str3"))
iter.remove();
}
Java Iteratorを使用せずにリストから削除する8つの方法は次のとおりです。
li.removeIf(<predicate>)
つまり.
List<String> li = new ArrayList<String>();
// ...
li.removeIf(st -> !st.equalsIgnoreCase("str3"));
この例外は、オブジェクトが別のスレッドによって同時に変更されたことを常に示すわけではないことに注意してください。 1つのスレッドがオブジェクトのコントラクトに違反する一連のメソッド呼び出しを発行すると、オブジェクトはこの例外をスローする場合があります。たとえば、フェイルファストイテレータを使用してコレクションを反復処理中にスレッドがコレクションを直接変更する場合、イテレータはこの例外を処理します。
http://download.Oracle.com/javase/1.4.2/docs/api/Java/util/ConcurrentModificationException.html から取得
はい、人々はそれに出くわします-問題は、それを反復している間はリストを変更できないことです。私は過去に2つの選択肢を使用しました:
これらのオプションは、削除する要素を見つけるためにリストを反復処理する必要があることを前提としています。リスト要素がテスト対象のプロパティを持つ複雑なオブジェクトである場合に便利です。
特定のケースでは、removeAllを使用するだけなので、反復する必要さえありません。 APIをご覧ください こちら 。引数にないものをすべて破棄するretainAllのような気の利いたメソッドもあります。リスト内のオブジェクトがequalsおよびhashcodeを適切に実装する場合はいつでも、remove/retain-likeメソッドを使用できます。アプリ内のインスタンス間の同等性を識別するためにequals/hashcodeに依存できない場合は、自分で削除する必要があります。
For-eachループで、要素を削除するリストのコピーを直接作成できます。私にとって、それが最も簡単な方法です。このようなもの:
for (String stringIter : new ArrayList<String>(myList)) {
myList.remove(itemToRemove);
}
それがあなたを助けることを願っています。
Java 8バージョンに言及する価値があると思います
@Test
public void testListCur() {
List<String> li = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
li.add("str" + i);
}
li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList());
System.out.println(li);
}
これを試してください(Java 8):
list.removeIf(condition);
ArrayListにはmodCount
フィールドがあります-コレクション変更のカウント
メソッドiterator()
を呼び出すと、新しいオブジェクトItr
が作成されます。 expectedModCount
フィールドがあります。 expectedModCount
フィールドは、modCount
値で初期化されます。呼び出すとき
li.remove("str3");
modCount
インクリメント。いつイテレータ経由でli
へのアクセスを試みますかexpectedModCount == modCount
falseの場合はConcurrentModificationException
をスローします
したがって、イテレータを取得し、コレクションが変更された後-イテレータは無効とみなされ、使用できません。
私はこの問題を手に入れましたが、簡単な方法はhvgotcodesが提供した2番目の方法と同じだと思います。
または、繰り返しながら保持したいものをすべて新しいリストにコピーし、完了したら古いリストを破棄できます。
@Test
public void testListCur(){
List<String> li=new ArrayList<String>();
for(int i=0;i<10;i++){
li.add("str"+i);
}
List<String> finalLi = new ArrayList<String>();
for(String st:li){
if(st.equalsIgnoreCase("str3")){
// Do nothing
} else {
finalLi.add(st);
}
}
System.out.println(finalLi);
}
私は最高の答えはbigdev.deからだと思いますが、私はそれに何かを追加したいと思います(アイテムがリストから削除された場合、おそらくどこかに何かを記録したいかもしれません):
List<String> list = new ArrayList<>();
list.removeIf(a -> {
boolean condition = a.equalsIgnoreCase("some condition");
if(condition)
logger.info("Item removed from the list: " + a);
return condition;
});
私は別の方法でループしました...
public void testListCur(){
List<String> li=new ArrayList<String>();
for(int i=0;i<10;i++){
li.add("str"+i);
}
for(int i=0; i<li.size(); i++)
if(li.get(i).equalsIgnoreCase("str3"))
li.remove(i--);
System.out.println(li);
}