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. 为什么不HashSet尊重的CompareTo方法。
  2. HashSet是拥有唯一对象列表的最佳方式吗?

它使用一个IEqualityComparer<T>EqualityComparer<T>.Default除非你指定一个不同的构造)。

当你将一个元素添加到集合中时,它将使用IEqualityComparer<T>.GetHashCode查找哈希代码,并存储哈希代码和元素(在检查元素是否已经在集合中)。

要查找元素,首先使用IEqualityComparer<T>.GetHashCode查找哈希码,然后对于具有相同哈希码的所有元素,将使用IEqualityComparer<T>.Equals来比较实际的相等性。

请注意,这不是一个有序的比较 – 这是有道理的,因为肯定有情况下,您可以轻松地指定相等,但不是一个总的顺序。 这基本上和Dictionary<TKey, TValue>

如果你想要一个使用sorting而不仅仅是相等比较的集合,你应该使用.NET 4中的SortedSet<T> – 它允许你指定一个IComparer<T>而不是IEqualityComparer<T> 。 这将使用IComparer<T>.Compare – 如果您使用Comparer<T>.Default ,它将委托给IComparable<T>.CompareToIComparable.CompareTo

下面是关于部分答案的说明: HashSet<T>的对象types不必实现IEqualityComparer<T> ,而只需要重写Object.GetHashCode()Object.Equals(Object obj)

而不是这个:

 public class a : IEqualityComparer<a> { public int GetHashCode(a obj) { /* Implementation */ } public bool Equals(a obj1, a obj2) { /* Implementation */ } } 

你做这个:

 public class a { public override int GetHashCode() { /* Implementation */ } public override bool Equals(object obj) { /* Implementation */ } } 

这很微妙,但是这让我在一天的好一段时间里试图让HashSet按照预期的方式工作。 和其他人一样, HashSet<a>会在使用set时根据需要调用a.GetHashCode()a.Equals(obj)

HashSet使用EqualsGetHashCode()

CompareTo是用于有序集合。

如果你想要独特的对象,但你不关心他们的迭代顺序, HashSet<T>通常是最好的select。

构造器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"); } } }