当空间大于32位时,如何实现GetHashCode兼容的Equals方法?

时间:2009-07-20 23:16:19

标签: .net equals gethashcode iequatable

在.NET中,您需要Equals(object)和GetHashCode()兼容。但有时你不能:

public class GreaterThan32Bits
{
    public int X { get; set; }
    public int Y { get; set; }
}

因为数据密度大于32位,并且GetHashCode返回Int32,所以您将有3个解决方案(假设正确实现了GetHashCode):

  1. 避免代码重复 丢弃为不正确

    public override bool Equals(object other)
    {
        if(ReferenceEquals(null, other)) return false;
        if(ReferenceEquals(this, other)) return true;
        return this.GetHashCode() == other.GetHashCode();
    }
    
  2. 与GetHashCode()

    分开实施Equals
    public override bool Equals(object obj)
    {
        if(ReferenceEquals(null, other)) return false;
        if(ReferenceEquals(this, other)) return true;
        var other = obj as GreaterThan32Bits;
        if(this.X == other.X) return this.Y == other.Y;
        return false;
    }
    
  3. 实现更高精度的GetHashCode64,重写的GetHashCode(32位)将返回(int)GetHashCode64(),而Equals将返回this.GetHashCode64()== other.GetHashCode64()

  4. 你会实施哪一个?

    第一个解决方案 imprecise 不正确,但更清晰。第二个选项看起来很干净,但是当类有更多属性时会变得非常复杂。第三种选择是妥协。

4 个答案:

答案 0 :(得分:5)

要求如下: 如果(a.Equals(b)),那么a.GetHashCode()== b.GetHashCode()

不是相反。

你不应该在GetHashCode()方面实现Equals()。 GetHashCode完全有效,但Equals()不得返回误报。

我建议这样做:

public override int GetHashCode()
{
    return unchecked( this.X * p1 + this.Y * p2 );
}

public override bool Equals(object obj) 
{
    var other = obj as GreaterThan32Bits;
    // you must do the null test after the cast, otherwise the
    // function crashes when obj is not a GreaterThan32Bits instance
    if (ReferenceEquals(other, null)) return false;
    return this.X == other.X && this.Y == other.Y;
}

其中p1和p2是大质数。这通常会产生良好的散列函数(少数散列碰撞 - >字典变得高效)。如果X和Y值是独立的(例如,你不希望像X = Y这样的直线上有很多点),那么像X ^ Y这样简单的东西就可以成为一个很好的哈希函数。

但是,如果你真的将这个类用作字典(或其他哈希表)中的键,那么你只需要一个好的哈希函数。

实际上,总是在GetHashCode()中返回0并且只实现Equals()是完全没错的。 对于像键这样的对象,词典仍然可以正常工作,它只会效率低下。

答案 1 :(得分:4)

您的第一个实现是正确。即使对象本身不相等,两个对象的哈希码也可能相等:这是哈希码的本质。

对象哈希码可能对确定两个对象相等时有用,但要确定它们 是否相等,则必须调用.Equals()

GetHashCode()总是返回0的实现是合法的,但当将该类型的对象插入到各种类型的容器中时,可能效率不高。

您的选项2是最佳选择。将Equals()的实现与GetHashCode()分开是一个好主意,因为它们做了很多不同的事情。当且仅当两个对象在所有方面都相同时,Equals()必须返回true。为此,您通常必须单独检查每个对象属性。

答案 2 :(得分:2)

严格来说,第一个解决方案不起作用。那不是解决方案。

哈希的想法完全不同。 Int32足以达到这个目的。

建议的GetHashCode()是

return X ^ Y;

简直就是这样。

编辑:等于方法然后可以使用GetHashCode(),但仅在哈希值不同时返回false。无论如何都需要深入比较。

答案 3 :(得分:1)

我认为你缺少的关键是GetHashCode()不必返回唯一值。

两个不同的对象返回相同的GetHashCode是完全可以接受的。假设您将两个对象添加到具有相同HashCode的HashSet,然后容器将首先使用GetHashCode查找对象所在的HashSet中的大致位置,然后在所有匹配对象上使用equals来查找您的确切对象。

如果每个对象都有唯一的哈希码,显然会更好。如果每个对象返回相同的hashCode,那么性能会很糟糕。