我有一个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
不尊重a
的{{1}}方法。CompareTo
是获得唯一对象列表的最佳方式吗?答案 0 :(得分:111)
它使用IEqualityComparer<T>
(EqualityComparer<T>.Default
,除非您在构造时指定不同的一个。)
当你向集合中添加一个元素时,它将使用IEqualityComparer<T>.GetHashCode
找到哈希码,并存储哈希码和元素(当然,在检查元素是否已经在集合中之后)。
要查找元素,它将首先使用IEqualityComparer<T>.GetHashCode
来查找哈希码,然后对于具有相同哈希码的所有元素,它将使用IEqualityComparer<T>.Equals
来比较实际的相等性。 / p>
这意味着您有两种选择:
IEqualityComparer<T>
传递给构造函数。如果您无法修改T
本身,或者您想要非默认的相等关系(例如“具有负用户ID的所有用户被视为相等”),则这是最佳选项。这几乎从未在类型本身上实现(即Foo
没有实现IEqualityComparer<Foo>
),而是在一个仅用于比较的单独类型中。GetHashCode
和Equals(object)
,在类型本身中实现相等性。理想情况下,也要在类型中实现IEquatable<T>
,特别是如果它是值类型。这些方法将由默认的相等比较器调用。请注意,这与有序比较无关,这是有道理的,因为在某些情况下您可以轻松指定相等但不是总排序。这基本上与Dictionary<TKey, TValue>
完全相同。
如果你想要一个使用排序而不仅仅是相等比较的集合,你应该使用.NET 4中的SortedSet<T>
- 它允许你指定一个IComparer<T>
而不是IEqualityComparer<T>
。这将使用IComparer<T>.Compare
- 如果您使用的是IComparable<T>.CompareTo
,则会IComparable.CompareTo
或Comparer<T>.Default
。
答案 1 :(得分:69)
以下是对未提及的答案的一部分的澄清:HashSet<T>
的对象类型不必实现IEqualityComparer<T>
,而只需覆盖Object.GetHashCode()
和{ {1}}。
而不是:
Object.Equals(Object obj)
你这样做:
public class a : IEqualityComparer<a>
{
public int GetHashCode(a obj) { /* Implementation */ }
public bool Equals(a obj1, a obj2) { /* Implementation */ }
}
这很微妙,但是这让我感到沮丧,因为一天中的大部分时间都试图让HashSet按照预期的方式运行。和其他人一样,public class a
{
public override int GetHashCode() { /* Implementation */ }
public override bool Equals(object obj) { /* Implementation */ }
}
在处理集合时会根据需要调用HashSet<a>
和a.GetHashCode()
。
答案 2 :(得分:9)
HashSet
使用Equals
和GetHashCode()
。
CompareTo
适用于有序集。
如果您想要唯一的对象,但不关心它们的迭代顺序,HashSet<T>
通常是最佳选择。
答案 3 :(得分:4)
构造函数HashSet接收对象用于添加新对象的IEqualityComparer实现。 如果您在HashSet中使用方法,则需要覆盖Equals,GetHashCode
//*[@id="loginidtext"]