我通常让Eclipse为我生成hashCode()方法,但是现在我发现有迹象表明生成的哈希代码可能不是很好。
在哈希集中使用由Eclipse生成的hash code()方法返回的哈希代码会导致查找速度比使用手工编码的hashCode()方法慢6倍。
这是我的测试:
代码:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyPojo {
private final int a;
private final int b;
private final int c;
public MyPojo(int a, int b, int c) {
super();
this.a = a;
this.b = b;
this.c = c;
}
public static void main(String[] args) {
List<MyPojo> listOfPojos = new ArrayList<MyPojo>();
Set<MyPojo> setOfPojos = new HashSet<MyPojo>();
for (int countA = 0; countA < 100; countA++) {
for (int countB = 0; countB < 100; countB++) {
for (int countC = 0; countC < 100; countC++) {
MyPojo myPojo = new MyPojo(countA, countB, countC);
listOfPojos.add(myPojo);
setOfPojos.add(myPojo);
}
}
}
long startTime = System.currentTimeMillis();
for (int count = 0; count < 10; count++) {
for (MyPojo myPojo : listOfPojos) {
if (!setOfPojos.contains(myPojo)) {
throw new RuntimeException();
}
}
}
long endTime = System.currentTimeMillis();
System.out.format("Execution time: %3f s", (endTime - startTime) / 1000.0);
}
// Generated by Eclipse
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + a;
result = prime * result + b;
result = prime * result + c;
return result;
}
// Generated by Eclipse
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyPojo other = (MyPojo) obj;
if (a != other.a)
return false;
if (b != other.b)
return false;
if (c != other.c)
return false;
return true;
}
}
在我的计算机上,这导致执行时间约为 1.23 秒。
现在用我在其他地方找到的这个方法替换hashCode()方法:
@Override
public int hashCode() {
final int magic = 0x9e3779b9;
int seed = 0;
seed ^= this.a + magic + (seed << 6) + (seed >> 2);
seed ^= this.b + magic + (seed << 6) + (seed >> 2);
seed ^= this.c + magic + (seed << 6) + (seed >> 2);
return seed;
}
现在执行时间只有0.2秒,快了6倍左右!
为什么?
编辑:
正如所建议的那样,计算哈希值的“重新货币”数量会得到以下结果:
用Eclipse生成hashCode()方法:
1: 62
2: 62
3: 1406
4: 440
5: 62
6: 1406
7: 62
8: 440
9: 52094
10: 4670
11: 4670
12: 26144
13: 1358
14: 1358
15: 1358
16: 2716
使用手工编码的hashCode()方法:
1: 79093
2: 180316
3: 23444
4: 107020
5: 2213
6: 6821
7: 296
8: 960
10: 12
所以Eclipse生成的方法只给出了62个只发生过一次的哈希码。
手编版给了79093只出现一次的哈希码,180316只出现两次的哈希码
点差相当不同。
编辑2:
还尝试了Objects.hash(…),与Eclipse生成的hashCode()方法相比,它给出了相同的“重复出现”计数。
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
此外,这实际上更减慢了执行速度:1.38秒
编辑3:
下面是对上述更好散列编码方法中的“幻数”的来源的解释:
boost::hash_combine中的幻数
编辑4:使用http://projectlombok.org生成hashCode()和equals()方法
龙目岛给出了迄今为止最好的结果:
1: 33958
2: 146124
3: 8118
4: 162360
Execution time: 0.187000 s
EclipsehashCode()
遵循有效Java
中建议的准则,作者说该方法相当不错,但绝对不是最好的方法。
如果< code>hashCode的性能不令人满意,您可以自由地寻找替代品。
我还想提到的一件事是,对于几乎每个hashCode
函数,您可能能够找到一些阻止函数均匀分布哈希值的数据集,从而使代码中的HashSet
像一个长列表一样工作。
你可以在这里看到其他讨论:Eclipse生成的哈希码函数有什么好处吗?
您可能还想阅读此内容
我使用eclipse生成Object的hashCode和equals方法的覆盖,并生成了一些关于hashCode覆盖的问题。下面的hashCode()是否正确? 问题: -为什么eclipse会生成两行代码?我认为将两个结果相加是合适的。知道为什么它们是分开的任务吗? -最终的int素数可以是任何素数吗? -整数结果是否应始终为 1?
问题内容: 我有一些Java代码要翻译成Scala。 该代码由一些不可变的类组成,这些类适合Scala中的目的。 但我不希望引入错误,所以我想,以确保所生成的代码,并为/行为等同于目前的实现。 我已经看过“ Scala编程”,但只说 第三,编译器将方法的“自然”实现添加到String,hashCode,并等于您的类。 问题答案: Scala有一个编译器选项,您可以使用它来获取“内部使用的后键入源代
问题内容: 某些哈希表方案(例如布谷鸟哈希或动态完美哈希)依赖于通用哈希函数的存在以及能够收集表现出冲突的数据并通过从通用哈希函数系列中选择一个新的哈希函数来解决这些冲突的能力。 。 不久前,我试图在以杜鹃哈希为后盾的Java中实现哈希表,并遇到了麻烦,因为尽管所有Java对象都有一个函数,但返回的值对于每个对象都是固定的(当然,除非对象更改)。这意味着如果没有用户提供外部家族的通用哈希函数,就不
我正在创建使用Facebook登录SDK的Android应用程序。 我想生成调试密钥哈希。在Facebook网站上我发现了这样的命令: keytool-exportcert-alias androiddebugkey-keystore c:\users\redio\.android\debug.keystore“c:\openssl\bin\openssl”sha1-binary“c:\opens
问题内容: 为什么不: 代替: 获得唯一哈希码的更高机会? 问题答案: 因为数组的最大长度为。 由于的主要用途是确定将对象插入/ 的后备数组中的哪个插槽,因此hashcode> 将无法存储在该数组中。
Eclipse源菜单有一个“generate hashCode/equals method”,它生成如下函数。 如果我在生成和时选择多个字段,Eclipse使用上面显示的相同模式。 我不是散列函数的专家,我想知道生成的散列函数有多“好”?在什么情况下它会发生故障并导致太多碰撞?