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.count
是2
,但是:
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.count
是3
。
- 为什么不
HashSet
尊重的CompareTo
方法。 -
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>.CompareTo
或IComparable.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
使用Equals
和GetHashCode()
。
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"); } } }