为啥 java hashCode() 中经常使用异或,而很少使用其他位运算符?

Posted

技术标签:

【中文标题】为啥 java hashCode() 中经常使用异或,而很少使用其他位运算符?【英文标题】:Why are XOR often used in java hashCode() but another bitwise operators are used rarely?为什么 java hashCode() 中经常使用异或,而很少使用其他位运算符? 【发布时间】:2011-01-21 00:15:20 【问题描述】:

我经常看到类似的代码

int hashCode()
  return a^b;

为什么要异或?

【问题讨论】:

可能重复 --> ***.com/questions/1379952/… 【参考方案1】:

在所有位操作中,XOR 具有最好的位混洗特性。

这个真值表解释了原因:

A B AND
0 0  0
0 1  0
1 0  0
1 1  1

A B OR
0 0  0
0 1  1
1 0  1
1 1  1

A B XOR
0 0  0
0 1  1
1 0  1
1 1  0

正如您所见,AND 和 OR 在混合位方面做得很差。

OR 将平均产生 3/4 个一位。另一方面,AND 将平均产生 3/4 空位。只有 XOR 具有偶数位与空位分布。这使得它对于哈希码生成非常有价值。

请记住,对于散列码,您希望尽可能多地使用键信息并获得散列值的良好分布。如果你使用 AND 或 OR,你会得到偏向于有很多零的数字或有很多一的数字的数字。

【讨论】:

【参考方案2】:

异或有以下优点:

它不依赖于计算顺序,即 a^b = b^a 它不会“浪费”位。如果您在其中一个组件中更改一点,最终值也会发生变化。 速度很快,即使在最原始的计算机上也只需一个周期。 它保持均匀分布。如果你组合的两部分是均匀分布的,那么组合也是如此。换句话说,它不会将摘要的范围压缩成更窄的范围。

更多信息here.

【讨论】:

异或运算不会浪费位如果所有输入位都是独立的,但是如果它合并强相关的位,它可能会浪费很多。例如,如果一个类型表示 0-65535 范围内的一对数字,并通过将数字异或在一起形成哈希,则每个值中为零的高 16 位在哈希码中将为零。更糟糕的是,如果不成比例的实例数量(例如 10%)有两个数字匹配,那么相同比例的实例将返回零作为哈希值。【参考方案3】:

XOR 运算符是可逆的,即假设我有一个位串为0 0 1,我将它与另一个位串1 1 1 异或,输出为

0 xor 1 = 1
0     1 = 1
1     1 = 0

现在我可以再次将第一个字符串与结果进行异或,以获得第二个字符串。即

0   1 = 1
0   1 = 1
1   0 = 1

所以,这使第二个字符串成为键。其他位运算符未发现此行为

更多信息请看这里 --> Why is XOR used on Cryptography?

【讨论】:

hashCode 不需要倒置。 //对不起我的英语不好 是的,但 XOR 在密码学中的一种用途是其可逆性。【参考方案4】:

还有另一个用例:对象必须在其中比较(某些)字段而不考虑它们的顺序。例如,如果您希望一对 (a, b) 始终等于一对 (b, a)。 XOR 具有a ^ b = b ^ a 的性质,所以在这种情况下可以用在哈希函数中。

示例:(完整代码here)

定义:

final class Connection 
    public final int A;
    public final int B;

    // some code omitted

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Connection that = (Connection) o;

        return (A == that.A && B == that.B || A == that.B && B == that.A);
    

    @Override
    public int hashCode() 
        return A ^ B;
    

    // some code omitted

用法:

        HashSet<Connection> s = new HashSet<>();
        s.add(new Connection(1, 3));
        s.add(new Connection(2, 3));
        s.add(new Connection(3, 2));
        s.add(new Connection(1, 3));
        s.add(new Connection(2, 1));

        s.remove(new Connection(1, 2));

        for (Connection x : s) 
            System.out.println(x);
        

        // output:
        // ConnectionA=2, B=3
        // ConnectionA=1, B=3

【讨论】:

以上是关于为啥 java hashCode() 中经常使用异或,而很少使用其他位运算符?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在java的set集合中 hashcode相同 但equals结果可能为false

JAVA中重写equals方法为啥要重写hashcode方法说明

为啥重写equals方法,一定要重写HashCode方法?

java 基础笔记--hashCode(),你好,为啥要重写

为啥我需要覆盖 Java 中的 equals 和 hashCode 方法?

为啥我需要覆盖 Java 中的 equals 和 hashCode 方法?