乘法应该是次优的.为什么在hashCode中使用它

  • 本文关键字:hashCode java hash hashcode
  • 更新时间 :
  • 英文 :


哈希函数非常有用,用途广泛。通常,它们用于将一个空间映射到一个小得多的空间。当然,这意味着两个对象可能会散列到相同的值(碰撞(,但这是因为你在减少空间(鸽子洞原理(。函数的效率在很大程度上取决于哈希空间的大小。

令人惊讶的是,许多Java hashCode函数都在使用乘法来生成新对象的哈希代码,如下所示(creating-a-hashCode-method-Java(

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

如果我们想在同一范围内混合两个散列码,xor应该比加法好得多,我认为这是传统上使用的。如果我们想增加空间,移位一些字节,然后进行异或运算仍然是有意义的。我猜乘以31几乎与将一个哈希值移1然后相加相同,但效率应该低得多。。。

不过,由于这是推荐的方法,我想我遗漏了一些东西。所以我的问题是为什么会这样?

注:

  • 我不是在问我们为什么要用素数。很明显,如果我们使用乘法,我们应该使用素数。然而,乘以任何数字,即使是素数,对xor来说仍然是次优的。这就是为什么所有其他非加密哈希函数以及大多数加密函数都使用xor而不是乘法
  • 我确实没有迹象表明(除了那些众所周知的散列函数(xor会更好。事实上,正是因为它被广泛接受,我怀疑乘以素数和和在实践中应该同样好,而且更好。我在问为什么
  • Java中的int类型可用于表示-2147483648到2147483647之间的任何整数
  • 有时,对象的哈希码可能是其内存地址(这很有意义,在很多情况下都很有效((如果从例如对象继承(

答案是不同因素的混合:

  • 在现代体系结构中,在给定的指令管道中,执行乘法与移位所花费的时间最终可能无法测量——这更多地与CPU上相关执行单元的可用性有关,而不是与"移位"有关;生的";所花费的时间
  • 在实践中,当在日常编程中与标准集合库集成时,散列函数的正确性通常更为重要;足够好";并且在IDE中易于自动化,而不是尽可能完美
  • 集合库通常会在后台添加辅助散列函数和潜在的其他技术,以克服原本较差的散列函数的一些弱点
  • 对于可调整大小的集合,一个有效的哈希函数的目标是将其哈希分散在任意大小的哈希表的可用范围内(尽管正如我所说,它将从内置的辅助函数中获得帮助(:乘以一个";魔术;常数通常是实现这一点的一种廉价方法(或者,即使乘法比移位贵一点:考虑到好处,仍然足够便宜(;加法而不是XOR可能有助于稍微允许这种"雪崩"效应。(在大多数实际情况下,你可能会发现它们同样有效。(
  • 您通常可以假设JIT编译器";知道";大约等于移位5位和减去1而不是乘以31。只是因为你写了"*31〃;在源代码中并不意味着它将被编译成乘法指令。(不过,在实践中,这可能是因为不管你怎么想,乘法指令在所讨论的体系结构上可能平均"更快"……在这种情况下,通常最好让你的代码坚持所需的逻辑,并让JIT编译器处理低级别优化。(

相关内容

  • 没有找到相关文章

最新更新