布尔字段的哈希码实现

时间:2015-02-24 03:35:51

标签: java hashcode

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

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

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

其他一切都是一样的。然后这两个不等对象的哈希码是相同的。我知道这种情况还可以。但想象一下,如果有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;
    }
}

3 个答案:

答案 0 :(得分:8)

您有几个选择:

选项1:位标记

保证的最佳方法永远不会布尔哈希之间的冲突是使用类似于bit flagging中使用的技术,你让每个布尔占据自己的位。例如:

// `byte` can be replaced with `short`, `int`, or `long` to fit all of your variables.
byte = 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 shows in his answer扩展了选项1的设计,作为容纳多个字段的更简单方法。作为Adrian Shum commentedthis answer提供了“常规哈希算法”的概述,该算法旨在独立于您尝试哈希的内容而有效。

基本思想是将每种类型的简化哈希值乘以一些任意大的素数,以确保每个哈希值都是唯一的(尽管证明了这一点可以避免我)。例如:

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只是一些任意素数。您可以轻松使用3711323456789。但是,使用较大的被乘数会有一些权衡,即您的哈希值会更快超过Integer.MAX_VALUE并使哈希值无效。

答案 1 :(得分:4)

当你有两个甚至更多的布尔值时,哈希码算法已经处理好了。

看得更近一点:

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

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

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

答案 2 :(得分:1)

组合多个字段时,一个好的做法是从第一个字段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;
}

您可以在code generated by Wire(Prococol Buffers实现)中看到一个真实的例子。

参考文献: