一尘不染

HashSet如何比较元素是否相等?

c#

我有一堂课是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.count3

  1. 为什么不HashSet尊重aCompareTo方法。
  2. HashSet拥有唯一对象列表的最佳方法是吗?

阅读 527

收藏
2020-05-19

共1个答案

一尘不染

它使用一个IEqualityComparer<T>EqualityComparer<T>.Default除非您在构造上指定了另一个)。

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

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

这意味着您有两个选择:

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

请注意,从 顺序 比较的角度来看,这些都不是什么-这很有意义,因为在某些情况下,您可以轻松指定相等性但不能指定总顺序。Dictionary<TKey, TValue>基本上与都一样。

如果要使用 排序 而不是相等比较的集合,则应SortedSet<T>从.NET 4 使用-
允许您指定一个IComparer<T>而不是一个IEqualityComparer<T>。这将使用IComparer<T>.Compare-将委派给,IComparable<T>.CompareTo或者IComparable.CompareTo如果您正在使用Comparer<T>.Default

2020-05-19