11

我的理解是,您通常应该将 xor 与 GetHashCode() 一起使用来生成一个 int,以通过其值(而不是通过其引用)来识别您的数据。这是一个简单的例子:

class Foo
{
    int m_a;
    int m_b;

    public int A
    {
        get { return m_a; }
        set { m_a = value; }
    }

    public int B
    {
        get { return m_b; }
        set { m_b = value; }
    }

    public Foo(int a, int b)
    {
        m_a = a;
        m_b = b;
    }

    public override int GetHashCode()
    {
        return A ^ B;
    }

    public override bool Equals(object obj)
    {
        return this.GetHashCode() == obj.GetHashCode();
    }
}

这个想法是,我想根据属性 A 和 B 的值将 Foo 的一个实例与另一个实例进行比较。如果 Foo1.A == Foo2.A 和 Foo1.B == Foo2.B,那么我们就有相等性。

这是问题所在:

Foo one = new Foo(1, 2);
Foo two = new Foo(2, 1);

if (one.Equals(two)) { ... }  // This is true!

这些都为 GetHashCode() 生成值 3,导致 Equals() 返回 true。显然,这是一个简单的示例,并且只有两个属性,我可以简单地比较 Equals() 方法中的各个属性。但是,对于更复杂的类,这将很快失控。

我知道有时只设置一次哈希码并始终返回相同的值是很有意义的。但是,对于需要评估相等性的可变对象,我认为这是不合理的。

在实现 GetHashCode() 时处理可以轻松互换的属性值的最佳方法是什么?

也可以看看

覆盖 System.Object.GetHashCode 的最佳算法是什么?

4

7 回答 7

30

首先 - 不要仅根据 GetHashCode() 实现 Equals() - 即使对象不相等,哈希码有时也会发生冲突。

GetHashCode() 的合约包括以下内容:

  • 不同的哈希码意味着对象绝对不相等
  • 相同的哈希码意味着对象可能相等(但可能不相等)

Andrew Hare 建议我合并他的回答:

我建议您阅读此解决方案(顺便说一下,由我们自己的Jon Skeet提供)以“更好”地计算哈希码。

不,以上内容相对较慢,并没有太大帮助。有些人使用 XOR(例如 a ^ b ^ c),但我更喜欢 Josh Bloch 的“Effective Java”中显示的那种方法:

public override int GetHashCode()
{
    int hash = 23;
    hash = hash*37 + craneCounterweightID;
    hash = hash*37 + trailerID;
    hash = hash*37 + craneConfigurationTypeCode.GetHashCode();
    return hash;
}

23 和 37 是互质的任意数。

与 XOR 方法相比,上述方法的好处是,如果您的类型具有两个经常相同的值,则对这些值进行异或运算将始终给出相同的结果 (0),而上述方法将区分它们,除非您非常不走运。

正如上面的片段中提到的,您可能还想查看Joshua Bloch 的书 Effective Java,其中包含对该主题的很好的处理(哈希码讨论也适用于 .NET)。

于 2009-06-17T18:05:28.247 回答
2

Andrew 发布了一个很好的示例来生成更好的哈希码,但也请记住,您不应该使用哈希码作为相等性检查,因为它们不能保证是唯一的。

举一个简单的例子来说明为什么这是一个双重对象。它比 int 具有更多可能的值,因此不可能为每个双精度数设置唯一的 int。散列实际上只是第一步,在需要快速找到键的情况下使用,例如通过首先比较散列,可以排除大部分可能的键,只有具有匹配散列的键需要花费完全相等检查(或其他冲突解决方法)。

于 2009-06-17T18:04:08.717 回答
1

散列总是涉及冲突,您必须处理它(fe,比较散列值,如果它们相等,则精确比较类内的值以确保类相等)。

使用简单的 XOR,您会遇到很多冲突。如果您想要更少,请使用一些数学函数将值分布在不同的位上(位移、与素数相乘等)。

于 2009-06-17T18:06:45.283 回答
1

阅读可变对象的覆盖 GetHashCode?C#并考虑实现IEquatable<T>

于 2009-06-17T18:07:17.730 回答
1

有几个更好的哈希实现。 例如FNV 哈希。

于 2009-06-17T18:16:13.643 回答
0

出于好奇,因为哈希码通常不是比较的好主意,只执行以下代码会不会更好,或者我错过了什么?

public override bool Equals(object obj)
{
    bool isEqual = false;
    Foo otherFoo = obj as Foo;
    if (otherFoo != null)
    {
        isEqual = (this.A == otherFoo.A) && (this.B == otherFoo.B);
    }
    return isEqual;
}
于 2009-06-17T18:09:03.530 回答
0

哈希的快速生成和良好分布

public override int GetHashCode()
{
    return A.GetHashCode() ^ B.GetHashCode();         // XOR
}
于 2014-01-30T23:54:08.380 回答