125

したがって、反復中にJava HashSetから要素を削除しようとすると、 ConcurrentModificationExceptionが発生します。次の例のように、 HashSetから要素のサブセットを削除する最良の方法は何ですか?

Set<Integer> set = new HashSet<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

// Throws ConcurrentModificationException
for(Integer element : set)
    if(element % 2 == 0)
        set.remove(element);

これが解決策ですが、あまりエレガントではないと思います:

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

for(Integer element : set)
    if(element % 2 == 0)
        removeCandidates.add(element);

set.removeAll(removeCandidates);

ありがとう!

4

7 に答える 7

193

セットの要素を手動で反復できます。

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element % 2 == 0) {
        iterator.remove();
    }
}

forループではなくループを使用するこのパターンがよく見られwhileます。

for (Iterator<Integer> i = set.iterator(); i.hasNext();) {
    Integer element = i.next();
    if (element % 2 == 0) {
        i.remove();
    }
}

人々が指摘したようにfor、反復子変数 (iこの場合) をより小さなスコープに限定するため、ループの使用が推奨されます。

于 2009-07-10T15:55:20.883 に答える
25

を取得する理由ConcurrentModificationExceptionは、エントリがIterator.remove()ではなくSet.remove()を介して削除されるためです。反復の実行中にSet.remove()によってエントリが削除されると、ConcurrentModificationException が発生します。一方、この場合、反復中のIterator.remove()によるエントリの削除がサポートされています。

新しい for ループは便利ですが、残念ながらこの場合は機能しません。Iterator 参照を使用できないためです。

反復中にエントリを削除する必要がある場合は、Iterator を直接使用する長い形式を使用する必要があります。

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}
于 2009-07-12T04:54:31.023 に答える
10

最初のループを削除してソリューションをリファクタリングすることもできます。

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>(set);

for(Integer element : set)
   if(element % 2 == 0)
       removeCandidates.add(element);

set.removeAll(removeCandidates);
于 2009-07-10T16:01:07.380 に答える
5

より現代的なストリームのアプローチは次のとおりです。

myIntegerSet.stream().filter((it) -> it % 2 != 0).collect(Collectors.toSet())

ただし、これにより新しいセットが作成されるため、非常に巨大なセットの場合、メモリの制約が問題になる可能性があります。

編集: この回答の以前のバージョンでは Apache CollectionUtils が提案されていましたが、それはスチームが登場する前のことでした。

于 2009-07-10T16:08:17.910 に答える
2

他の可能な解決策:

for(Object it : set.toArray()) { /* Create a copy */
    Integer element = (Integer)it;
    if(element % 2 == 0)
        set.remove(element);
}

または:

Integer[] copy = new Integer[set.size()];
set.toArray(copy);

for(Integer element : copy) {
    if(element % 2 == 0)
        set.remove(element);
}
于 2010-02-21T13:34:51.407 に答える