【问题标题】:GetHashCode() problem using xor使用 xor 的 GetHashCode() 问题
【发布时间】:2010-11-03 18:20:36
【问题描述】:

我的理解是,您通常应该将 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() 时处理可以轻松互换的属性值的最佳方法是什么?

另见

What is the best algorithm for an overridden System.Object.GetHashCode?

【问题讨论】:

    标签: c# .net gethashcode


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      我建议您阅读this solution(顺便说一下,我们自己的Jon Skeet),以获得“更好”的计算哈希码的方法。

      不,上面的比较慢,而且 没有多大帮助。有些人用 XOR(例如 a ^ b ^ c)但我更喜欢 Josh Bloch 中显示的一种方法 《有效的 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)而上述将 区分它们,除非 你真倒霉。

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

      【讨论】:

      • 这是一个非常好的答案,并突出了一些要点。但是它缺乏一个例子。如果您愿意,请编辑您的答案以包含我的示例(或类似内容),我将删除我的答案。 (顺便说一句,+1)。
      • 完成 - 结果是 wiki 的答案。
      • “此解决方案”的链接已失效,here is the new link
      • 这需要在未选中的块中吗?
      • @ja72:是的,它可能应该在一个未经检查的块中。默认值为未选中,但编译器选项(或运行时环境设置?)可以更改该默认值。
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          出于好奇,因为哈希码通常不适合进行比较,所以最好只执行以下代码,还是我遗漏了什么?

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

          【讨论】:

          • 是的,但是如果我尝试比较 50 个属性,这将变得非常难看。我希望只实现 GetHashCode() ,然后让 Equals() 简单易用。看来这可能是个白日梦。
          【解决方案6】:

          有几个更好的哈希实现。以FNV hash 为例。

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-11-10
              • 2015-01-16
              • 2012-11-02
              • 1970-01-01
              • 2011-01-20
              • 2010-11-11
              • 1970-01-01
              相关资源
              最近更新 更多