6

您能否用简单的英语解释一下 XOR ( ^) 运算符是什么以及它在以下代码中的作用:

public int GetHashCode(Box bx)
{
    int hCode = bx.Height ^ bx.Length ^ bx.Width;
    return hCode.GetHashCode();
} 
4

2 回答 2

13

XOR 代表异或。它确保 A 或 B 中的任何一个都为真,但绝不是两者都为真。在这种情况下,我们正在执行按位运算,因此您可以制作一个漂亮的结果小图,它们如下所示;

0 ^ 1 = 1
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 0 = 0

由于您将其应用于整数,因此上述结果将应用于操作数中的每一位。因此,假设您的高度、长度和宽度分别为 1、2、3。

你首先会有

0001 ^ 0010 导致 0011 然后将异或成 3 所以 0011 ^ 0011 给你 0000

编辑:提供评论中的 wiki 链接以补充我的解释;http://en.wikipedia.org/wiki/Exclusive_or#Computer_science

编辑:为什么会0001 ^ 0010导致0011

所以最好一点一点地做到这一点。考虑一下运算符迭代两组位并比较它们的对。所以在这种情况下,让我们从右到左工作(在这种情况下对大多数人来说最不重要)。

1 ^ 0 = 1 // xxx1
0 ^ 1 = 1 // xx11
0 ^ 0 = 0 // x011
0 ^ 0 = 0 // 0011  - end of input

如此拼凑起来,你就得到了0011。基本上,获取每对输入并参考真值表以获得结果。注释显示的输出x是一个尚未计算的值。

关于碰撞,是的,在这种情况下有很多碰撞。如果我说它是独一无二的,那是一个糟糕的词选择。我真正的意思是,如果你有 2、8、4 作为你的值 XOR'n 它们按这个顺序总是会产生相同的值。

于 2013-08-14T18:48:34.527 回答
3

详细说明一下,您会XOR在方法中的字段之间看到很多 ing,getHashCode()因为它是获取对象签名的安全方法。签名的概念是它就像一个对象的指纹,它需要适合 32 位。此签名被许多对象用作快速比较,(但是,如果您打算为此使用它,请查看该维基百科文章,因为您需要注意相等性和哈希码),或者对于一些一种寻址方式(如 .netDictionary和 Java 的情况HashMap)。

对我来说,获取 Box 指纹的明显解决方案是简单地将这些值相加,这样如果它们中的任何一个发生变化,你就会得到一个不同的指纹: bx.Height + bx.Length + bx.Width

如果我们需要测试两个盒子的相等性,那么相等操作可能非常昂贵(即非常慢):

  • Box {5, 10, 15}
  • Box {30, 40, 50}

我们可以比较两个哈希码,看看它们不同,然后跳过完全相等比较,而不是进行完全相等比较。在字典中,这对于给我们一个快速的方法来找到一个bin(一个元素)来放置对象是至关重要的。

但是如果这些值中的任何一个太高,我们可能会得到一个整数溢出异常,所以我们不使用加法,而是使用 XOR。另一种解决方案,对于 C# 来说相当独特,是使用unchecked{ ... }块,但使用 XOR 被认为更优雅。

我们可以做一件更微妙的事情来提高性能,您会在许多自动生成的哈希码方法(例如由 ReSharper 或 IntelliJ 生成的方法)中看到这一点:我们可以通过移动(相乘)每个价值。

    public int hashCode() {
        int result = x;
        result = 31 * result ^ y;
        result = 31 * result ^ z;
        return result;
    }

现在发生的事情是,哈希码中的每个字段实际上都在生成的 32 位中占有一席之地。这意味着这两个框:

  • Box {1, 20, 30}
  • Box {1, 30, 20}

不会具有相同的哈希码(它们与您当前的系统具有相同的哈希码,但它们不同!)

关于哈希码,你想知道的比你想知道的要多,但我还要说一件事。

在 Java/Scala 和 .net 框架中,如果重载 equals 或 hash-code,则还必须重载另一个. 您还必须确保如果两个对象 A 和 B 具有不同的哈希码,则对 A.Equals(B) 的调用必须为 false。

于 2013-08-14T19:15:39.050 回答