質問
このバグを見つけるのに時間がかかりました...
次の方法を考えてみましょう。
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
set.remove(obj)
}
空ではないハッシュ セットを使用してメソッドを呼び出しましたが、要素は削除されません。
それはなぜでしょうか?
解決
HashSetのために、これはそれがセットに追加されたオブジェクトのハッシュコードが変更された場合の後に発生する可能性があります。 HashSet.remove()メソッドは、誤ったハッシュバケツに見て、それを見つけるために失敗することがあります。
あなたがiterator.removeをしなかった場合は、これはおそらく起こらないだろう()、しかし、どのような場合には、そのhashCodeを変更することができますHashSetの中でオブジェクトを格納することは(あなたが発見したとして)起こるのを待って、事故です。
他のヒント
パズル? Object.hashCode
、Object.equals
または "ハッシュセットが" 正しく実装された場合(例えば、java.net.URL
を参照 - 使用URI
)。
また、奇数何かが(従属月の実装と位相が正確に何である)が起こる可能性があります。
セットの実装タイプとセット内に含まれるオブジェクトは何ですか?
- HashSet の場合は、オブジェクトの hashCode() メソッドの値が次の期間に一定であることを確認してください。
set.put(...)
そしてset.remove(...)
. - TreeSet の場合は、セットのコンパレータまたはオブジェクトのコンパレータに影響を与える変更がオブジェクトに加えられていないことを確認してください。
compareTo
方法。
どちらの場合も、間のコードは、 set.put(...)
そして set.remove(...)
それぞれのクラス実装によって定義された規約に違反します。経験則として、不変オブジェクトをセット コンテンツ (およびマップ キー) として使用することをお勧めします。その性質上、このようなオブジェクトはセット内に保存されている間は変更できません。
他のセット実装を使用している場合は、そのコントラクトについて JavaDoc を確認してください。しかし、通常はどちらか equals
または hashCode
オブジェクトがセットに含まれている間は同じままでなければなりません。
を超えて不足しています ';' set.remove(obj)
後に、それは(javadocをより引用)の3つの状況で発生する可能性があります。
ClassCastException - if the type of the specified element is incompatible with this set (optional). NullPointerException - if the specified element is null and this set does not support null elements (optional). UnsupportedOperationException - if the remove method is not supported by this set.
また、試すことができます:
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
iterator.remove();
}
次のようになるはずです:
public void foo(Set<Object> set)
{
Iterator i = set.iterator();
i.next();
i.remove();
}
?
バグは以下に関係している可能性があります。
パブリック void 削除()
この方法を呼び出す以外に、反復が進行中に根底にあるコレクションが変更されている場合、イテレーターの動作は不特定です。
(参照)
私はこの問題は、セットが値ではなく、参照渡しされていることである助けることが(の一部)ことを感じることができません。でも、私はJavaで多くの経験を持っていないので、私は完全に間違っている可能性があります。