HashSet如何比较元素的相等性?

时间:2012-01-21 09:39:37

标签: c# 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不尊重a的{​​{1}}方法。
  2. CompareTo是获得唯一对象列表的最佳方式吗?

4 个答案:

答案 0 :(得分:111)

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

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

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

这意味着您有两种选择:

  • 将自定义IEqualityComparer<T>传递给构造函数。如果您无法修改T本身,或者您想要非默认的相等关系(例如“具有负用户ID的所有用户被视为相等”),则这是最佳选项。这几乎从未在类型本身上实现(即Foo没有实现IEqualityComparer<Foo>),而是在一个仅用于比较的单独类型中。
  • 通过覆盖GetHashCodeEquals(object),在类型本身中实现相等性。理想情况下,也要在类型中实现IEquatable<T>,特别是如果它是值类型。这些方法将由默认的相等比较器调用。

请注意,这与有序比较无关,这是有道理的,因为在某些情况下您可以轻松指定相等但不是总排序。这基本上与Dictionary<TKey, TValue>完全相同。

如果你想要一个使用排序而不仅仅是相等比较的集合,你应该使用.NET 4中的SortedSet<T> - 它允许你指定一个IComparer<T>而不是IEqualityComparer<T>。这将使用IComparer<T>.Compare - 如果您使用的是IComparable<T>.CompareTo,则会IComparable.CompareToComparer<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使用EqualsGetHashCode()

CompareTo适用于有序集。

如果您想要唯一的对象,但不关心它们的迭代顺序,HashSet<T>通常是最佳选择。

答案 3 :(得分:4)

构造函数HashSet接收对象用于添加新对象的IEqualityComparer实现。 如果您在HashSet中使用方法,则需要覆盖Equals,GetHashCode

//*[@id="loginidtext"]
相关问题