布尔字段上的哈希码实现

问题描述 投票:0回答:3

如果有两个布尔字段,如何实现一个好的哈希码?通常人们只是将整数值添加到他们的哈希码值中。但如果我简单地在哈希码中添加 1 或 0,我认为这不好。因为如果我有两个 A 类对象:

obj1.b = true,obj1.c = false。

obj2.b = false,obj2.c = true。

其他都一样。那么这两个不相等的对象的hashcode是相同的。我知道这种情况没问题。但想象一下,如果有 100 个布尔字段,那么碰撞会太多,对吧?我不希望这么多不同的物体落入同一个桶中。

我下面所做的是将不同的数字分配给每个字段的不同真值,因此对象哈希码可以非常不同。

public class A {

    private final String a;
    private final boolean b;
    private final boolean c;

...

@Override public int hashCode(){
            int i,j;
            if(b) {
                    i = 10;
            }
            else {
                    i = 0;
            }
            if(c) {
                    j = 88;
            }
            else {
                    j = 3;
            }
            int result = 0;
            result = 31*result + i + j;
            result = 31*result + (a != null ? a.hashCode() : 0);
            return result;
    }
}
java hashcode
3个回答
9
投票

您有几个选择:

选项 1:位标记

保证布尔值之间永远不会发生冲突的最佳方法是使用类似于“位标记”中使用的技术,让每个布尔值占据自己的位。例如: // `byte` can be replaced with `short`, `int`, or `long` to fit all of your variables. byte booleans = 0; if(bool1) booleans += 1; // 0001 if(bool2) booleans += 2; // 0010 if(bool3) booleans += 4; // 0100 if(bool4) booleans += 8; // 1000 ... 但是,这种方法在处理大量布尔值时很快就会变得低效,并且高度依赖于目标数组的大小。例如,如果您有一个大小为 16 的目标数组,则只有前 4 个对哈希值有影响(因为最大索引为 1111)。

对此的两个解决方案是增加目标数组的大小(这可能不受您的控制),或者确保布尔值的顺序从可变参数最多到最少。这些都不是最佳的,因此这种方法既快速又简单,但在实践中不是很有效。

选项 2:改变碱基的哈希值

Pham Trung 在他的答案中展示的设计

扩展了选项 1,作为容纳多个字段的更简单的方法。正如

Adrian Shum 评论的那样

这个答案

提供了“通用哈希算法”的概述,该算法的设计目的是独立于您尝试哈希的内容而有效。

基本思想是将每种类型的简化哈希值乘以某个任意大的素数,以确保每个哈希值都是唯一的(尽管我无法证明这一点)。例如: int result = 0; result = 31*result + bool1 ? 1 : 0; result = 31*result + bool2 ? 1 : 0; ... 对于更稀疏的哈希分布,您可以将其与

Boolean.hashCode

结合使用,如其他答案所示:
int result = 0;
result += 31*result + bool1.hashCode();
result += 31*result + bool2.hashCode();
...

此解决方案的优点在于它可以应用于其他类型,就像您在示例代码中已经拥有的那样:

...
result = 31*result + i;
result = 31*result + (a != null ? a.hashCode() : 0);
result = 31*result + my_complex_object.hashCode();

注意
:在这些例子中,

31

只是一些任意素数。您可以轻松使用
37

11323456789

。然而,使用更大的被乘数是有代价的,即你的散列将更快地超过 
Integer.MAX_VALUE
 并使你的散列无效。

当你有两个甚至更多布尔值时,哈希码算法已经处理好了。
再仔细看一下:

// Very simple example public int hashCode() { int result = 31; for(boolean val : booleanList) { // This is the important part: result = 31 * result + Boolean.hashCode(val); } return result; }

5
投票

注意

for循环的主要部分,在这种情况下,我们以不同的方式对待每个布尔值,因为我们总是将结果乘以31(或任何好的质数),然后再将其添加到结果中。

如果我们将整个哈希码可视化为一些
base 31
的数字,那么我们可以理解,每个布尔值的位置和值都被考虑到了最终结果中。每个布尔值都可以被视为哈希码中的数字,因此对于 case (true, false) 和 case (false, true),它们将具有两个不同的哈希码。

组合多个字段时,一个好的做法是从第一个字段的 hashCode 开始,然后将当前结果乘以素数,然后再添加每个字段的 hashcode:

@Override public int hashCode() { int result = 0; result = b1.hashCode(); result = result * 37 + b2.hashCode(); result = result * 37 + b3.hashCode(); // ... // result = result * 37 + bn.hashCode(); return result; }

您可以在

1
投票
(Prococol Buffers 实现)中看到一个真实的示例。

参考资料:

hashCode 方法的最佳实现(位于 StackOverflow)

Boolean.hashCode()
    (位于 StackOverflow)
  • 介绍电线
© www.soinside.com 2019 - 2024. All rights reserved.