Java.util.Set
実装は、重複する要素を削除します。
重複する要素はJava.util.Set
で内部的にどのように削除されますか?
実際には、ソースからのAFAIK JavaのほとんどのSet
実装は、要素がすでに含まれているかどうかさえチェックしません。
それらは常に、セット要素を保持する内部構造に対してadd()
を実行し、そのオブジェクトに重複の場合を処理させます。
例えばHashSet
は内部HashMap
でput(K,V)
を呼び出します。これは、重複している場合は古いエントリを上書きする新しいオブジェクトを挿入するだけです。
あなたの質問を少し読んで、あなたはJava.util.HashSet
(通常は誰もがデフォルトで使用するもの)で奇妙な振る舞いを見ていると思います。
Java.util.Set
のコントラクトとは異なり、次のようにJava.util.HashSet
で同じオブジェクトを2回取得することができます。
import Java.util.HashSet;
import Java.util.Set;
public class SetTest
{
public static void main(String[] args)
{
MyClass myObject = new MyClass(1, "testing 1 2 3");
Set<MyClass> set = new HashSet<MyClass>();
set.add(myObject);
myObject.setHashCode(2);
set.add(myObject);
System.out.println(set.size()); // this will print 2.
}
private static class MyClass
{
private int hashCode;
private String otherField;
public MyClass(int hashCode, String otherField)
{
this.hashCode = hashCode;
this.otherField = otherField;
}
public void setHashCode(int hashCode)
{
this.hashCode = hashCode;
}
public boolean equals(Object obj)
{
return obj != null && obj.getClass().equals(getClass()) && ((MyClass)obj).otherField.equals(otherField);
}
public int hashCode()
{
return hashCode;
}
}
}
@jitterからのポインタとソースを確認すると、なぜこれが発生するのかがわかります。
@jitterが言うように、Java.util.HashSet
は内部でJava.util.HashMap
を使用します。ハッシュが最初と2番目の間で変化する場合addJava.util.HashMap
で別のバケットが使用され、オブジェクトはセットに2回含まれます。
コードサンプルは少し不自然に見えるかもしれませんが、ハッシュが可変フィールドから作成され、equalsメソッドがそれらのフィールドと同期されていないドメインクラスでこれが実際に発生するのを見てきました。
これを見つける簡単な方法は、興味のあるコードのソースを調べることです。
各JDKには、パブリッククラスのソースコードを含むsrc.Zipが含まれているため、HashSetのソースを見つけて確認することができます:)これにはEclipseをよく使用します。それを開始し、新しいJavaプロジェクトを作成し、JVMをインストールされたJDKに設定し(そうでない場合は、src.Zipを持たないシステムデフォルトのJREを使用している場合)、Ctrl-Shiftを押します。 -TはHashSetに移動します。
あなたの質問をもっと詳しく読んでください:
Java doc for Set.add()から、重複を追加することはできませんか、それともaddAllを意味しますか?:
指定された要素がまだ存在しない場合は、このセットに追加します(オプションの操作)。より正式には、(e == null?e2 == null:e.equals(e2))のような要素e2がセットに含まれていない場合、指定された要素eをこのセットに追加します。このセットにすでに要素が含まれている場合、呼び出しはセットを変更せずに残し、falseを返します。これは、コンストラクターの制限と組み合わせて、セットに重複する要素が含まれないようにします。