web-dev-qa-db-ja.com

Java.util.Setの要素が重複しています

Java.util.Set実装は、重複する要素を削除します。

重複する要素はJava.util.Setで内部的にどのように削除されますか?

11
Neo

実際には、ソースからのAFAIK JavaのほとんどのSet実装は、要素がすでに含まれているかどうかさえチェックしません。

それらは常に、セット要素を保持する内部構造に対してadd()を実行し、そのオブジェクトに重複の場合を処理させます。

例えばHashSetは内部HashMapput(K,V)を呼び出します。これは、重複している場合は古いエントリを上書きする新しいオブジェクトを挿入するだけです。

14
jitter

あなたの質問を少し読んで、あなたは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メソッドがそれらのフィールドと同期されていないドメインクラスでこれが実際に発生するのを見てきました。

11
Nick Holt

これを見つける簡単な方法は、興味のあるコードのソースを調べることです。

各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を返します。これは、コンストラクターの制限と組み合わせて、セットに重複する要素が含まれないようにします。

0
Tomas