web-dev-qa-db-ja.com

ConcurrentModificationExceptionを使用せずにfor-eachループを使用して反復中にコレクションを変更する方法は?

For-eachループを使用して反復処理中にCollectionを変更すると、ConcurrentModificationExceptionが返されます。回避策はありますか?

21
aps

使用する - Iterator#remove

これは、反復中にコレクションを変更する唯一の安全な方法です。詳細については、「 コレクションインターフェイス チュートリアル」を参照してください。

反復中に要素をaddする機能も必要な場合は、 ListIterator を使用します。

38
mre

回避策の1つは、ループ後に変更を保存し、追加/削除することです。

例えば:

List<Item> toRemove = new LinkedList<Item>();

for(Item it:items){
    if(remove){
        toRemove.add(it);
    }
}
items.removeAll(toRemove);
11
jzd

2番目の回避策は、イテレータが例外を与えないコレクションクラスを使用することです。たとえば、 ConcurrentLinkedQueueConcurrentHashMap など。

これらは、反復子に一貫性の弱いモデルを提供することにより、例外をスローする必要を回避します。 (もちろん、これらのモデルを理解し、それらがアプリケーションに適しているかどうかを判断する必要があります。)

通常、これらは非並行コレクションよりも少し遅くなりますが、重大な競合がある場合は同期コレクションラッパーよりも速くなります。

3
Stephen C

コレクションから要素を削除するだけの場合は、Iterableの代わりにIteratorを使用できます。

それ以外の場合、できることは元のコレクションを反復するのではなく、最初にリストのコピーを作成することです。たとえば、コレクションがリストの場合、新しいArrayList(originaList)を作成し、それを反復処理できます。元のリストに変更を加える必要があります。

あなたのユースケースにより良いかもしれない別の選択肢は、for-eachではなく、伝統的なfor-を使用することです。

1
nanda