对于相同的对象,Java哈希代码在一种情况下会发生冲突,而在另一种情况下不会发生冲突,为什么?(代码如下)

对于相同的对象,Java哈希代码在一种情况下会发生冲突,而在另一种情况下不会发生冲突,为什么?(代码如下),java,algorithm,collections,hashmap,hash-code-uniqueness,Java,Algorithm,Collections,Hashmap,Hash Code Uniqueness,我试图编写一个小程序来演示java中只有equals被重写而不是hashcode()方法时的哈希冲突。这是为了证明两个不相等的对象可以具有相同哈希代码的理论。这是一个询问行为的面试问题 我创建了200000个对象,将它们存储在一个数组中,然后比较它们,看看哪些是重复的。(为此,在对象创建阶段之后,我使用嵌套For循环在对象数组上迭代。)对于大约200000个对象,我得到9次碰撞。第一个是索引196和121949处的对象。然后我继续打印这些散列码,以显示这两个值是相同的 然而,我得到了一些非常令人

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

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

然而,我得到了一些非常令人惊讶的行为。如果我迭代嵌套的for循环并打印hashcode的第一个冲突,我会得到相同的hashcode值

1867750575
1867750575 
对于索引196和121949处的对象

但是如果我注释掉用于检测所有冲突的嵌套for循环,并直接为索引196和121949处的元素打印hashcode,我会得到

1829164700
366712642
请注意,我不是在注释这些元素的创建,只是在检查碰撞的部分

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


附录1:据我所知,根据生日原则,如果我创建了200000个对象,我必须得到一个冲突,如何在每个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<EmployeeFactory, EmployeeFactory> hm = new HashMap<EmployeeFactory, EmployeeFactory>();
        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());

    }

}
未注释循环输出

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

如果不重写
hashCode()
,则会得到从
类对象继承的标识哈希代码函数

身份散列代码取决于每次运行程序时理论上看不到的东西,比如对象在内存中的位置、在您之前创建的对象数量等。您不能期望程序或方法的不同运行之间的身份散列值有任何一致性


但是,如果您运行完全相同的程序两次,并且不会太大,那么很有可能两次都使用相同的哈希值。但是,如果更改程序,则会更改加载和编译类所消耗的内存量,这很可能会通过更改对象在内存中的位置来更改标识哈希值。

Matt Timmermans的回答相当好地涵盖了基本问题,尤其是“你不能期望在不同的跑步之间有任何一致性。”(+1)

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


还要注意,标识hashcode是在第一次调用
Object.hashcode()
System.identityHashCode()时延迟生成的
并且该值存储在对象中,以便后续对此对象的调用将返回相同的值。如果在另一个线程中运行冲突检测器循环,则会得到完全不同的哈希值,从而产生不同的冲突。

这是(相对而言的)两个不相等的对象具有相同的哈希代码很好。问题是当两个相等的对象具有不同的哈希代码时。在这种情况下,哈希映射根本不起作用。如果不重写哈希代码,则无法在对象上获得一致的哈希代码。因此,您正在运行两个不同的二进制文件,并想知道为什么它们的行为不同?对象将不会具有相同的哈希代码在数组中创建的第196个和第121949个对象中总是使用相同的hashCode有效。请注意,重写
equals
和not
hashCode
所导致的问题不是冲突。哈希代码可能会冲突。问题是您可以使用不同的哈希代码拥有相等的对象,这意味着您将无法在哈希映射等中找到它们。@SamiKuhmonen二进制文件与hashCode implem有什么关系entations?was哈希代码的计算将不会基于二进制文件更改,如果您查看默认的哈希代码实现,这两种情况下都是相同的,我只是注释了一个for循环。@Sameer是的,它是相同的,因为它可能会被计算,例如,基于对象的内存地址,正在运行的二进制文件可能会对它产生一定的影响t、 据我所知,根据生日原则,如果我创建200000 K对象,我必须得到一个冲突,迭代每个hascode如何改变任何事情?我没有说你不会得到冲突。我说散列码会不同。这意味着你会得到不同的冲突。而且,这不是真的对真正起作用的代码进行评级,就是添加代码来进行迭代。我尝试添加另一个大小为200000的数组,只是为了看看冲突索引是否发生了变化,它们没有发生变化,因此显然在循环未提交的情况下对二进制文件进行更改不会产生任何变化。因此,假设更改二进制文件会改变hashco取消剂量保持。@Sameer你没有用同样的方法更改二进制,注意。
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