反復中に HashSet から要素を削除する [重複]
質問
この質問にはすでに答えがあります:
したがって、Java から要素を削除しようとすると、 ハッシュセット 反復中に、私は ConcurrentModificationException. 。要素のサブセットを削除する最良の方法は何ですか? ハッシュセット 次の例のように?
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);
ありがとう!
解決
は、手動で集合の要素を反復処理することができます:
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
ループを使用することが好ましい。
他のヒント
を取得する理由 ConcurrentModificationException
エントリが次の方法で削除されるためです Set.remove() とは対照的に Iterator.remove(). 。エントリが次の方法で削除された場合 Set.remove() 反復の実行中に、ConcurrentModificationException が発生します。一方、エントリの削除は、 Iterator.remove() この場合、 while 反復がサポートされます。
新しい for ループは優れていますが、残念ながらこの場合は機能しません。Iterator 参照を使用できないためです。
反復中にエントリを削除する必要がある場合は、イテレータを直接使用する長い形式を使用する必要があります。
for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
Integer element = it.next();
if (element % 2 == 0) {
it.remove();
}
}
また、あなたのソリューション最初のループの除去をリファクタリングすることができます:
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);
Javaの8コレクションは物事が簡単で安全になりremoveIfと呼ばれる素敵な方法があります。 APIのドキュメントから:
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate.
Errors or runtime exceptions thrown during iteration or by the predicate
are relayed to the caller.
興味深いノート:
The default implementation traverses all elements of the collection using its iterator().
Each matching element is removed using Iterator.remove().
木材のように言った - 「Javaの8コレクションは、物事が簡単で安全になりremoveIfと呼ばれる素敵な方法を持っている」
ここにあなたの問題を解決するためのコードは次のとおりです。
set.removeIf((Integer element) -> {
return (element % 2 == 0);
});
今、あなたのセットには、唯一の奇数の値が含まれています。
これは、反復しながらする必要がありますか?あなたがやっているすべてのフィルタリングまたは選択された場合、私は、Apache Commonsの<のhref = "http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#filter(javaを使用することをお勧めし.util.Collection、%20org.apache.commons.collections.Predicate)」REL = "nofollowをnoreferrer"> CollectionUtilsする。そこにいくつかの強力なツールがあり、それはあなたのコードが行う「クーラー。」
ここにあなたが必要なものを提供しなければならないの実装です。
Set<Integer> myIntegerSet = new HashSet<Integer>();
// Integers loaded here
CollectionUtils.filter( myIntegerSet, new Predicate() {
public boolean evaluate(Object input) {
return (((Integer) input) % 2 == 0);
}});
あなたは頻繁にあなたがEVEN_NUMBER_PREDICATE
のようなものという名前を付けます...再利用のための静的変数にそれを引き出すことができ、述語の同じ種類を使用して自分自身を見つける場合。いくつかは、そのコードを見ると、「読みにくい」ことを宣言していますが、静的に述語を引き出したとき、それはきれいに見えることがあります。そして、それは我々がCollectionUtils.filter(...)
をやっていることを確認するのは簡単です、それはすべての創造以上のループの束よりも(私にとって)より読みやすいようです。
他の可能な解決策ます:
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);
}