1268

次の理由により、次のことができないことは誰もが知っていますConcurrentModificationException

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

しかし、これは明らかに時々機能しますが、常に機能するとは限りません。ここにいくつかの特定のコードがあります:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

もちろん、これにより次の結果が得られます。

Exception in thread "main" java.util.ConcurrentModificationException

複数のスレッドがそれを行っていないにもかかわらず。ともかく。

この問題の最善の解決策は何ですか? この例外をスローせずにループ内のコレクションからアイテムを削除するにはどうすればよいですか?

また、ここでは任意のものを使用していますが、Collection必ずしも ではないため、ArrayListに依存することはできませんget

4

30 に答える 30

1654

Iterator.remove()安全です。次のように使用できます。

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Iterator.remove()反復中にコレクションを変更する唯一の安全な方法であることに注意してください。反復の進行中に基になるコレクションが他の方法で変更された場合、動作は規定されていません。

ソース: docs.oracle > コレクション インターフェイス


同様に、 があり、アイテムを追加ListIteratorしたい場合は、 を使用できます。使用できるのと同じ理由で、使用 できるように設計されています。ListIterator#addIterator#remove


あなたの場合、リストから削除しようとしましたが、その内容を繰り返してputいる間にしようとすると、同じ制限が適用されます。Map

于 2008-10-21T23:27:15.203 に答える
353

これは機能します:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

foreach ループは反復のためのシンタックス シュガーであるため、反復子を使用しても役に立たないと思いましたが、この.remove()機能が得られます。

于 2008-10-21T23:26:31.383 に答える
233

Java 8 では、新しいremoveIfメソッドを使用できます。あなたの例に適用されます:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);
于 2014-05-28T10:11:44.693 に答える
44

質問は既に回答されているため、イテレータ オブジェクトの remove メソッドを使用するのが最善の方法です。エラー"java.util.ConcurrentModificationException"がスローされた場所の詳細に進みます。

next()すべてのコレクション クラスには、Iterator インターフェイスを実装し、 、 、 などのメソッドを提供するプライベート クラスがありremove()ますhasNext()

次のコードは次のようになります...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

ここで、メソッドcheckForComodificationは次のように実装されます

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

ご覧のとおり、明示的にコレクションから要素を削除しようとすると. とはmodCount異なる結果にexpectedModCountなり、例外が発生しますConcurrentModificationException

于 2010-05-15T19:57:56.713 に答える
28

言及したようにイテレータを直接使用するか、2番目のコレクションを保持して、削除する各アイテムを新しいコレクションに追加してから、最後にremoveAllを追加できます。これにより、for-each ループのタイプ セーフを使用し続けることができますが、メモリ使用量と CPU 時間が増加します (非常に大きなリストや非常に古いコンピューターがない限り、大きな問題にはなりません)。

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}
于 2008-10-21T23:32:17.427 に答える
19

そのような場合、一般的なトリックは(だった?)逆戻りすることです:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

removeIfそうは言っても、Java 8 で、たとえば、またはfilterストリームでより良い方法があることを嬉しく思います。

于 2014-08-29T09:56:15.023 に答える
17

for ループを使用したClaudiusと同じ答え:

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
    Object object = it.next();
    if (test) {
        it.remove();
    }
}
于 2013-08-21T12:39:45.337 に答える
12

既存のリストのコピーを作成し、新しいコピーを反復処理します。

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}
于 2012-06-26T05:28:35.773 に答える
12

Eclipse Collectionsでは、MutableCollectionremoveIfで定義されたメソッドが機能します。

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Java 8 Lambda 構文では、これは次のように記述できます。

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Java 8 ではインターフェイスにPredicates.cast()デフォルトremoveIfメソッドが追加されたため、ここでへの呼び出しが必要です。java.util.Collection

注:私はEclipse Collectionsのコミッターです。

于 2012-12-18T23:08:46.757 に答える
4

ConcurrentHashMapまたはConcurrentLinkedQueueまたはConcurrentSkipListMapは、アイテムを削除または追加しても ConcurrentModificationException をスローしないため、別のオプションになる場合があります。

于 2016-06-23T11:18:58.893 に答える
2

Java 同時変更例外

  1. シングルスレッド
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        list.remove(it.next()); //throws ConcurrentModificationException
    }
}

解決策: iteratorremove()メソッド

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}
  1. マルチスレッド
  • コピー/変換して、別の 1 つのコレクションを反復処理します。小さなコレクション向け
  • synchronize[約]
  • スレッド セーフ コレクション[概要]
于 2021-03-31T14:01:16.110 に答える
1

while ループを使用できます。

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}
于 2020-12-28T10:40:17.500 に答える
1

この質問は古すぎて Java 8 に関するものではないことは承知していますが、Java 8 を使用している場合は、removeIf() を簡単に使用できます。

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);
于 2018-09-26T20:15:21.577 に答える
0
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

内部 iterator. next() 呼び出しをスキップした場合、キャッチはリストから要素を削除した後です。それはまだ動作します!私はこのようなコードを書くことを提案しませんが、その背後にある概念を理解するのに役立ちます:-)

乾杯!

于 2016-06-30T07:24:49.550 に答える
0

これを試してください(リスト内の等しいすべての要素を削除しますi):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}
于 2019-11-13T19:15:38.360 に答える