web-dev-qa-db-ja.com

ArrayListを繰り返し処理して要素を削除するときにJava.util.ConcurrentModificationExceptionを回避する方法

繰り返したいArrayListがあります。それを繰り返しながら、私は同時に要素を削除する必要があります。明らかにこれはJava.util.ConcurrentModificationExceptionを投げます。

この問題に対処するためのベストプラクティスは何ですか?最初にリストを複製する必要がありますか?

ループ自体ではなく、コードの別の部分から要素を削除します。

私のコードはこのようになります:

public class Test() {
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff() {
        for (A a : abc) 
        a.doSomething();
    }

    public void removeA(A a) {
        abc.remove(a);
    }
}

a.doSomethingTest.removeA()を呼び出すことがあります。

163
anon

2つの選択肢

  • 削除したい値のリストを作成し、ループ内でそのリストにを追加し、最後にoriginalList.removeAll(valuesToRemove)を呼び出します
  • イテレータ自体でremove()メソッドを使用してください。これは拡張forループを使用できないことを意味します。

2番目のオプションの例として、リストから5を超える長さの文字列を削除します。

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String value = iterator.next();
    if (value.length() > 5) {
        iterator.remove();
    }
}
266
Jon Skeet

ArrayListのJavaDocsから

このクラスのiteratorメソッドとlistIteratorメソッドによって返される反復子は非常に高速です。反復子が作成された後でリストが構造的に変更されている場合は、反復子自身のremoveまたはadd以外の方法で methodsを指定すると、イテレータはConcurrentModificationExceptionをスローします。

16
Varun Achar

あなたは高度な "for loop"でリストから値を削除しようとしていますが、たとえトリックを適用したとしても不可能です。よりよい方法は、ここで推奨されているようにイテレータレベルをコーディングすることです。

人々がどのように伝統的なforループアプローチを提案していないのだろうか。

for( int i = 0; i < lStringList.size(); i++ )
{
    String lValue = lStringList.get( i );
    if(lValue.equals("_Not_Required"))
    {
         lStringList.remove(lValue);
         i--; 
    }  
}

これもうまくいきます。

9
suhas0sn07

あなたは本当に伝統的な方法で単に配列を反復するべきです

リストから要素を削除するたびに、それ以降の要素はプッシュフォワードになります。反復要素以外の要素を変更しない限り、次のコードが機能します。

public class Test(){
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff(){
        for(int i = (abc.size() - 1); i >= 0; i--) 
            abc.get(i).doSomething();
    }

    public void removeA(A a){
        abc.remove(a);
    }
}
8
Marcus

Java 8では、Collection Interfaceを使用し、removeIfメソッドを呼び出すことによってこれを実行できます。

yourList.removeIf((A a) -> a.value == 2);

より多くの情報が見つかります ここ

7
ggeo

通常の方法でループを実行してください。Java.util.ConcurrentModificationExceptionは、アクセスされる要素に関連するエラーです。

だから試してみてください。

for(int i = 0; i < list.size(); i++){
    lista.get(i).action();
}
6
Tacila

これは、削除するオブジェクトを追加するために別のリストを使用し、その後、元のリストから要素を削除するためにstream.foreachを使用する例です。

private ObservableList<CustomerTableEntry> customersTableViewItems = FXCollections.observableArrayList();
...
private void removeOutdatedRowsElementsFromCustomerView()
{
    ObjectProperty<TimeStamp> currentTimestamp = new SimpleObjectProperty<>(TimeStamp.getCurrentTime());
    long diff;
    long diffSeconds;
    List<Object> objectsToRemove = new ArrayList<>();
    for(CustomerTableEntry item: customersTableViewItems) {
        diff = currentTimestamp.getValue().getTime() - item.timestamp.getValue().getTime();
        diffSeconds = diff / 1000 % 60;
        if(diffSeconds > 10) {
            // Element has been idle for too long, meaning no communication, hence remove it
            System.out.printf("- Idle element [%s] - will be removed\n", item.getUserName());
            objectsToRemove.add(item);
        }
    }
    objectsToRemove.stream().forEach(o -> customersTableViewItems.remove(o));
}
5
serup

一つのオプションはこれにremoveAメソッドを変更することです -

public void removeA(A a,Iterator<A> iterator) {
     iterator.remove(a);
     }

しかしこれは、あなたのdoSomething()iteratorremoveメソッドに渡すことができるはずであることを意味します。あまり良い考えではありません。

これを2段階の方法で行うことができます。選択した要素を削除するのではなく、リストを反復処理する最初のループで、markそれらを削除対象とします。そのためには、これらの要素を別のListに単純にコピー(シャローコピー)することができます。

その後、繰り返しが終わったら、最初のリストからremoveAllを、2番目のリストのすべての要素を単純に実行します。

5
Bhaskar

リストを繰り返しながら、要素を削除したい場合は可能です。以下に私の例を見てみましょう。

ArrayList<String>  names = new ArrayList<String>();
        names.add("abc");
        names.add("def");
        names.add("ghi");
        names.add("xyz");

上記の配列リストの名前があります。そして私は上記のリストから "def"の名前を削除したいです。

for(String name : names){
    if(name.equals("def")){
        names.remove("def");
    }
}

上記のコードでは、反復中にリストを変更しているため、ConcurrentModificationException例外がスローされます。

このようにして、Arraylistから "def"という名前を削除するには、

Iterator<String> itr = names.iterator();            
while(itr.hasNext()){
    String name = itr.next();
    if(name.equals("def")){
        itr.remove();
    }
}

上記のコードでは、反復子を使用してArraylistから "def"という名前を削除し、配列を印刷しようとすると、次のような出力が表示されます。

出力:[abc、ghi、xyz]

4
Indra K

For each loopの代わりに、normal for loopを使用してください。たとえば、次のコードでは、Java.util.ConcurrentModificationExceptionを返さずに、配列リスト内のすべての要素を削除します。ユースケースに従ってループ内の条件を変更できます。

   for(int i=0;i<abc.size();i++)  {

          e.remove(i);
        }
3
Shubham Chopra

このような簡単なことをしてください。

for (Object object: (ArrayList<String>) list.clone()) {
    list.remove(object);
}
2
Xlsx

ストリームを使用した代替Java 8ソリューション

        theList = theList.stream()
            .filter(element -> !shouldBeRemoved(element))
            .collect(Collectors.toList());

Java 7では、代わりにGuavaを使用できます。

        theList = FluentIterable.from(theList)
            .filter(new Predicate<String>() {
                @Override
                public boolean apply(String element) {
                    return !shouldBeRemoved(element);
                }
            })
            .toImmutableList();

Guavaの例では不変のリストが表示されるので注意してください。

1
Zsolt Sky

ArrayListの代わりにCopyOnWriteArrayListを使用することもできます。これはJDK 1.5以降で推奨される最新のアプローチです。

0
Pathikreet

あなたの目標がリストからすべての要素を削除することであるなら、あなたはそれぞれのアイテムを反復処理してから呼び出すことができます:

list.clear()
0
Gibolt

配列リストの代わりにイテレータを使う

型が一致するセットをイテレータに変換する

そして次の要素に移動して削除する

Iterator<Insured> itr = insuredSet.iterator();
while (itr.hasNext()) { 
    itr.next();
    itr.remove();
}

次の要素に移動することは、要素を削除するためのインデックスが必要なので、ここで重要です。

0
user8009263

遅刻して私は知っていますが、私はこの解決策がシンプルでエレガントだと思うのでこれに答えます。

List<String> listFixed = new ArrayList<String>();
List<String> dynamicList = new ArrayList<String>();

public void fillingList() {
    listFixed.add("Andrea");
    listFixed.add("Susana");
    listFixed.add("Oscar");
    listFixed.add("Valeria");
    listFixed.add("Kathy");
    listFixed.add("Laura");
    listFixed.add("Ana");
    listFixed.add("Becker");
    listFixed.add("Abraham");
    dynamicList.addAll(listFixed);
}

public void updatingListFixed() {
    for (String newList : dynamicList) {
        if (!listFixed.contains(newList)) {
            listFixed.add(newList);
        }
    }

    //this is for add elements if you want eraser also 

    String removeRegister="";
    for (String fixedList : listFixed) {
        if (!dynamicList.contains(fixedList)) {
            removeResgister = fixedList;
        }
    }
    fixedList.remove(removeRegister);
}

これは一つのリストから他のリストへ更新するためのものであり、あなたは一つのリストからすべてを作ることができ、メソッドの更新であなたは両方のリストをチェックしてリスト間の要素を消すか追加することができます。これは両方のリストが常に同じサイズであることを意味します

「最初にリストを複製する必要がありますか?」

それが最も簡単な解決策になり、クローンから削除し、削除後にクローンをコピーします。

私のrummikubゲームからの例:

SuppressWarnings("unchecked")
public void removeStones() {
  ArrayList<Stone> clone = (ArrayList<Stone>) stones.clone();
  // remove the stones moved to the table
  for (Stone stone : stones) {
      if (stone.isOnTable()) {
         clone.remove(stone);
      }
  }
  stones = (ArrayList<Stone>) clone.clone();
  sortStones();
}
0
Arjen Rodenhuis