【问题标题】:bitwise operator >>> in hashCodehashCode 中的位运算符 >>>
【发布时间】:2013-10-28 08:06:39
【问题描述】:

我有两个相关的问题:

  1. 按位运算符 >>> 意味着我们将二进制数移动了这么多位,同时在最高有效位中填充 0。但是,为什么下面的操作会产生相同的数字:5>>>32 产生 5 而 -5>>>32 产生 -5。因为如果上面的描述是正确的,那么这两个操作都会产生 0 作为最终结果。

  2. 继续上面的内容,根据 Effective Java 书,我们应该在计算哈希码时使用 (int) (f ^ (f >>> 32))(如果字段很长)(如果字段很长)。我们为什么要这样做,解释是什么

【问题讨论】:

    标签: java hashcode bitwise-operators gethashcode effective-java


    【解决方案1】:

    5 可以表示为0101,如果你将它移动 1 位,即5>>>1,这将导致0010=2

    如果左侧操作数的提升类型是 int,则只有五个 右手操作数的最低位用作移位 距离。就好像右手操作数受到 按位逻辑 AND 运算符 & (§15.22.1),掩码值为 0x1f。 因此,实际使用的移位距离总是在 0 到 31,包括在内。

    当您使用 > 运算符和移位来移位整数时 距离大于等于32,你取移位距离 mod 32 (换句话说,你屏蔽掉除了低 5 位之外的所有 位移距离)。这可能非常违反直觉。例如 (i> >> 32) == i,对于每个整数 i。您可能期望它将整个数字向右移动,对于正输入返回 0 和 -1 对于负输入,但不是;它只是返回 i,因为 (i

    【讨论】:

    • 谢谢你的解释,但是第二个问题呢,为什么要使用 (int) (f ^ (f >>> 32)) 来计算hashCode(根据Effective Java book)
    • 长数据类型是 64 位二进制补码整数。有符号长整数的最小值为 -2^63,最大值为 2^63-1。在 Java SE 8 及更高版本中,您可以使用 long 数据类型来表示无符号的 64 位 long,它的最小值为 0,最大值为 2^64-1。 unsigned long 的最小值为 0,最大值为 2^64-1。每个右移将数字除以 2^K。而且你实际上不能用 Java 原始类型表示数字 2^63 = 9223372036854775808,因为这个数字大于最大的 long,而 long 是最大的原始类型。
    • 谢谢,根据avrilfanomar的回复,感觉(int) (f ^ (f >>> 32))的这个操作基本上是一种实现可能唯一数的方式(通过异或数字的前半部分和后半部分),然后将其转换为 int
    【解决方案2】:

    第一个问题的答案在这里why is 1>>32 == 1?

    简而言之,第二个问题的答案是,以这种方式使用整个 long 值(而不是它的一部分)并注意这可能是最快的方法。

    【讨论】:

    • 能否请您详细说明第二点,因为根据解释,如果是 long 我们可以考虑 0 到 63 的值。那么,为什么我们要移动 32 并与数字本身进行异或(在计算 hashCode 时)
    • 我们正在用 long 的第一个 32 位部分和第二个部分进行异或。这就是魔法=)
    【解决方案3】:

    我知道这个问题很久以前就得到了回答,但我尝试了一个例子来获得更多的澄清,我想其他人也会这样。

        long x = 3231147483648l;
        System.out.println(Long.toBinaryString(x));
        System.out.println(Long.toBinaryString(x >>> 32));
        System.out.println(Long.toBinaryString(x ^ (x >>> 32)));
        System.out.println(Long.toBinaryString((int) x ^ (x >>> 32)));
    

    这打印 -

    101111000001001111011001011110001000000000

    1011110000

    101111000001001111011001011110000011110000

    1001111011001011110000011110000

    正如@avrilfanomar 所提到的,这会将 long 的前 32 位与其他 32 位进行异或,无符号右移运算符可以帮助我们做到这一点。因为我们想在计算哈希码时使用这个长字段,所以直接将long 转换为int 意味着long 字段仅在高 32 位上有所不同将对哈希码贡献相同的值。这可能意味着仅在该字段中不同的两个对象将具有相同的哈希码,并将存储在同一个存储桶中(例如解决冲突的列表),这会影响基于哈希的集合的性能。因此,这个操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-14
      • 2012-11-09
      • 2010-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多