Java哈希码在一种情况下发生冲突而在另一种情况下不会发生碰撞,为什么? (以下代码)

我尝试编写一个小程序来演示java中的哈希冲突,只重写了equals而不是hashcode()方法。 这是为了certificate两个不等对象可以具有相同哈希码的理论。 这是针对行为问题的面试问题。

我创建了200,000个对象,将它们存储在一个数组中,然后将它们进行比较以查看哪些是重复的。 (为此我在对象创建阶段之后使用嵌套for循环迭代对象数组。)对于大约200,000个对象,我得到9次碰撞。 第一个是索引196和121949处的对象。然后我继续打印这些哈希码以显示两个值是相同的。

但是我得到了一些非常令人惊讶的行为。 如果我遍历嵌套的for循环并打印哈希码的第一次碰撞,我得到相同的哈希码值

1867750575 1867750575 

对于索引196和121949处的两个对象。

但是如果我注释掉嵌套for循环以检测所有冲突并直接打印索引196和121949的元素的哈希码,我得到

 1829164700 366712642 

注意,我没有评论这些元素的创建,只是我检查碰撞的部分。

为什么会发生这种情况,即使我不迭代它们,哈希码是不是应该一致?

附录1:据我所知,有没有一个消息来源,按照生日原则,如果我创建200,000个对象,我必须得到一个碰撞,如何迭代每个hascode或不改变任何东西?

附录2:我尝试添加另一个200000大小的数组,只是为了查看碰撞索引是否发生了变化,但是没有,所以显然在未提交循环的情况下对二进制文件进行更改不会进行任何更改。 因此,更改二进制更改哈希码的假设并不成立。

这是我的代码

 import java.util.HashMap; public class EmployeeFactory { private static int counter = 0; public int id; public String empName; EmployeeFactory() { id = counter; empName = "employee_" + id; counter++; } @Override public boolean equals(Object o) { // If the object is compared with itself then return true if (o == this) { return true; } if (o == null || o.getClass() != this.getClass()) { return false; } EmployeeFactory emp = (EmployeeFactory) o; // Compare the data members and return accordingly return this.id == emp.id; } public static void main(String[] args) { int Obj_Count = 200000; EmployeeFactory objs[] = new EmployeeFactory[Obj_Count]; for (int i = 0; i < Obj_Count; ++i) { EmployeeFactory temp = new EmployeeFactory(); objs[i] = temp; } //Please try code once un commenting the loop below and once while keeping it commented. /* for (int i = 0; i < Obj_Count; ++i) { for (int j = i + 1; j < Obj_Count; ++j) { if (objs[i].hashCode() == objs[j].hashCode()) { System.out.println("Objects with IDs " + objs[i].id + " and " + objs[j].id + " collided."); System.out.println("Object Is " + i + "and Obj ID is "+ objs[i].id + " Has Hashcode " + objs[i].hashCode()); System.out.println("Object Is " + j + "and Obj ID is "+ objs[j].id + " Has Hashcode " + objs[j].hashCode()); System.out.println(""); } } } */ HashMap hm = new HashMap(); objs[121949].id = objs[196].id; hm.put(objs[196], objs[196]); hm.put(objs[121949], objs[121949]); System.out.println(hm.get(objs[121949]).empName); System.out.println(hm.get(objs[196]).empName); // checking the hashmap System.out.println(hm.get(objs[121949]).hashCode()); System.out.println(hm.get(objs[196]).hashCode()); // Checking the array System.out.println(objs[121949].hashCode()); System.out.println(objs[196].hashCode()); } } 

结果输出:

 employee_121949 employee_196 1829164700 366712642 1829164700 366712642 

未评论的循环输出

 Objects with IDs 196 and 121949 collided. Object Is 196and Obj ID is 196 Has Hashcode 1867750575 Object Is 121949and Obj ID is 121949 Has Hashcode 1867750575 Objects with IDs 62082 and 145472 collided. Object Is 62082and Obj ID is 62082 Has Hashcode 2038112324 Object Is 145472and Obj ID is 145472 Has Hashcode 2038112324 Objects with IDs 62354 and 105841 collided. Object Is 62354and Obj ID is 62354 Has Hashcode 2134400190 Object Is 105841and Obj ID is 105841 Has Hashcode 2134400190 Objects with IDs 68579 and 186938 collided. Object Is 68579and Obj ID is 68579 Has Hashcode 1872358815 Object Is 186938and Obj ID is 186938 Has Hashcode 1872358815 Objects with IDs 105219 and 111288 collided. Object Is 105219and Obj ID is 105219 Has Hashcode 651156501 Object Is 111288and Obj ID is 111288 Has Hashcode 651156501 Objects with IDs 107634 and 152385 collided. Object Is 107634and Obj ID is 107634 Has Hashcode 273791087 Object Is 152385and Obj ID is 152385 Has Hashcode 273791087 Objects with IDs 108007 and 146405 collided. Object Is 108007and Obj ID is 108007 Has Hashcode 1164664992 Object Is 146405and Obj ID is 146405 Has Hashcode 1164664992 Objects with IDs 135275 and 180997 collided. Object Is 135275and Obj ID is 135275 Has Hashcode 996371445 Object Is 180997and Obj ID is 180997 Has Hashcode 996371445 Objects with IDs 153749 and 184310 collided. Object Is 153749and Obj ID is 153749 Has Hashcode 254720071 Object Is 184310and Obj ID is 184310 Has Hashcode 254720071 employee_121949 employee_121949 1867750575 1867750575 1867750575 1867750575 

Matt Timmermans的答案很好地涵盖了基本问题,特别是“你不能期望在不同的运行之间有任何一致性。” (1)

默认的Object.hashCode()实现(也称为身份哈希码,因为它与System.identityHashCode(obj)相同)目前在Hotspot中只是一个带有线程局部种子的伪随机数。 很长一段时间以来,对象的内存地址一直没有依赖。 如果您的程序执行完全确定,则哈希很可能是可重复的。

还要注意,在第一次调用Object.hashCode()System.identityHashCode() ,懒惰地生成了身份哈希码,并且该值存储在对象中,以便对该对象的后续调用将返回相同的值。 如果在另一个线程中运行碰撞检测器循环,则会得到完全不同的哈希值,从而产生不同的冲突。

当您不重写hashCode() ,您将获得从class Objectinheritance的标识哈希代码函数。

身份哈希码依赖于你无法看到的东西,理论上每次你运行你的程序都会改变,比如对象最终在内存中,在你之前创建的对象数等等。你不能指望有程序或方法的不同运行之间的身份哈希值的任何一致性。

但是,如果你运行完全相同的程序两次,并且它不是太大,那么两次最终会得到相同的哈希值的可能性非常大。 但是,如果更改程序,则可以通过加载和编译类来更改消耗的内存量,这很可能通过更改对象将在内存中的位置来更改标识哈希值。

Interesting Posts