使用xor的GetHashCode()问题

我的理解是你通常应该使用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,那么我们就有了相等性 .

Here's the problem:

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

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

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

我知道有时候只设置一次哈希码是很有意义的,并且总是返回相同的值 . 但是,对于需要对平等进行评估的可变对象,我认为这不合理 .

What's the best way to handle property values that could easily be interchanged when implementing GetHashCode()?

另请参阅重写的System.Object.GetHashCode的最佳算法是什么?

回答(7)

2 years ago

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

GetHashCode()的 Contract 包括以下内容:

  • 不同的哈希码意味着对象绝对不相等

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

Andrew Hare建议我加入他的答案:

我建议您阅读this solution(通过我们自己的Jon Skeet,顺便说一下)"better"方式来计算哈希码 .

不,以上相对较慢,并没有多大帮助 . 有些人使用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();
返回哈希;
}
23和37是任意数字,是共同素数 . 上述优于XOR方法的好处是,如果你的类型有两个经常相同的值,那么对这些值进行异或将总是给出相同的结果(0),而上面将区分它们,除非你非常不走运 .

如上面的代码片段所述,您可能还需要查看Joshua Bloch's book, Effective Java,,其中包含对主题的良好处理(哈希码讨论也适用于.NET) .

2 years ago

Andrew已经发布了一个生成更好的哈希代码的好例子,但是请记住,不应该使用哈希码作为相等检查,因为它们不能保证是唯一的 .

对于一个简单的例子,为什么这是一个双重对象 . 它具有比int更多的可能值,因此不可能为每个double赋予唯一的int . 哈希实际上只是第一遍,当你需要快速找到密钥时用于字典之类的情况,首先比较哈希可以排除大部分可能的密钥,只有匹配哈希的密钥需要有费用完全相等检查(或其他collision resolution方法) .

2 years ago

散列总是涉及冲突,你必须处理它(例如,比较散列值,如果它们相等,则完全比较类中的值以确保类相等) .

使用简单的XOR,您将获得许多碰撞 . 如果你想要更少,可以使用一些数学函数,在不同的位上分配值(位移,乘以素数等) .

2 years ago

阅读Overriding GetHashCode for mutable objects? C#并考虑实施 IEquatable<T>

2 years ago

快速生成和良好的散列分布

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

2 years ago

出于好奇,因为哈希码通常是一个糟糕的比较想法,只是做下面的代码不是更好,或者我错过了什么?

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;
}

2 years ago

有几种更好的哈希实现 . FNV hash例如 .