web-dev-qa-db-ja.com

HashSetはどのように要素が等しいかを比較しますか?

IComparableのクラスがあります:

public class a : IComparable
{
    public int Id { get; set; }
    public string Name { get; set; }

    public a(int id)
    {
        this.Id = id;
    }

    public int CompareTo(object obj)
    {
        return this.Id.CompareTo(((a)obj).Id);
    }
}

このクラスのオブジェクトのリストをハッシュセットに追加すると:

a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(a1);

すべてが正常で、ha.count2ですが、次のとおりです。

a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(new a(1));

現在、ha.count3です。

  1. HashSetaCompareToメソッドを尊重しないのはなぜですか。
  2. HashSetは、一意のオブジェクトのリストを作成する最良の方法ですか?
109
nima

IEqualityComparer<T> を使用します(構築時に別のものを指定しない限り、 EqualityComparer<T>.Default )。

要素をセットに追加すると、IEqualityComparer<T>.GetHashCodeを使用してハッシュコードを検索し、ハッシュコードと要素の両方を格納します(もちろん、要素が既にセットに含まれているかどうかを確認した後)。

要素を検索するには、最初にIEqualityComparer<T>.GetHashCodeを使用してハッシュコードを検索し、次に同じハッシュコードを持つすべての要素に対してIEqualityComparer<T>.Equalsを使用して実際の同等性を比較します。

つまり、次の2つのオプションがあります。

  • カスタムIEqualityComparer<T>をコンストラクターに渡します。これは、T自体を変更できない場合、またはデフォルト以外の等値関係が必要な場合(たとえば、「負のユーザーIDを持つすべてのユーザーが等しいと見なされる」)に最適なオプションです。これは、型自体にはほとんど実装されません(つまり、FooIEqualityComparer<Foo>を実装しません)が、比較にのみ使用される別の型に実装されます。
  • GetHashCodeEquals(object)をオーバーライドして、型自体に平等を実装します。特に値型の場合は、型にもIEquatable<T>を実装するのが理想的です。これらのメソッドは、デフォルトの等値比較器によって呼び出されます。

ordered比較の観点からこれがどれもないことに注意してください-合計ではなく、簡単に等式を指定できる状況は確かにあるため、これは理にかなっています注文。これは基本的にDictionary<TKey, TValue>と同じです。

単に等価比較の代わりにorderingを使用するセットが必要な場合は、 SortedSet<T> from .NET 4-を使用する必要がありますIComparer<T>ではなくIEqualityComparer<T>を指定します。これはIComparer<T>.Compareを使用します-IComparable<T>.CompareToを使用している場合は、IComparable.CompareToまたはComparer<T>.Defaultに委任します。

120
Jon Skeet

HashSetEqualsおよびGetHashCode()を使用します。

CompareToは順序付きセット用です。

一意のオブジェクトが必要であるが、その繰り返し順序は気にしない場合、通常、HashSet<T>が最良の選択です。

9
CodesInChaos

コンストラクターHashSetは、新しいオブジェクトを追加するためのIEqualityComparerを実装するオブジェクトを受け取ります。 HashSetでメソッドを使用したい場合、Equals、GetHashCodeをオーバーライドします。

namespace HashSet
{
    public class Employe
    {
        public Employe() {
        }

        public string Name { get; set; }

        public override string ToString()  {
            return Name;
        }

        public override bool Equals(object obj) {
            return this.Name.Equals(((Employe)obj).Name);
        }

        public override int GetHashCode() {
            return this.Name.GetHashCode();
        }
    }

    class EmployeComparer : IEqualityComparer<Employe>
    {
        public bool Equals(Employe x, Employe y)
        {
            return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower());
        }

        public int GetHashCode(Employe obj)
        {
            return obj.Name.GetHashCode();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer());
            hashSet.Add(new Employe() { Name = "Nik" });
            hashSet.Add(new Employe() { Name = "Rob" });
            hashSet.Add(new Employe() { Name = "Joe" });
            Display(hashSet);
            hashSet.Add(new Employe() { Name = "Rob" });
            Display(hashSet);

            HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer());
            hashSetB.Add(new Employe() { Name = "Max" });
            hashSetB.Add(new Employe() { Name = "Solomon" });
            hashSetB.Add(new Employe() { Name = "Werter" });
            hashSetB.Add(new Employe() { Name = "Rob" });
            Display(hashSetB);

            var union = hashSet.Union<Employe>(hashSetB).ToList();
            Display(union);
            var inter = hashSet.Intersect<Employe>(hashSetB).ToList();
            Display(inter);
            var except = hashSet.Except<Employe>(hashSetB).ToList();
            Display(except);

            Console.ReadKey();
        }

        static void Display(HashSet<Employe> hashSet)
        {
            if (hashSet.Count == 0)
            {
                Console.Write("Collection is Empty");
                return;
            }
            foreach (var item in hashSet)
            {
                Console.Write("{0}, ", item);
            }
            Console.Write("\n");
        }

        static void Display(List<Employe> list)
        {
            if (list.Count == 0)
            {
                Console.WriteLine("Collection is Empty");
                return;
            }
            foreach (var item in list)
            {
                Console.Write("{0}, ", item);
            }
            Console.Write("\n");
        }
    }
}
4
Nikolai Nechai