簡體   English   中英

對於相同的對象,Java哈希碼在一種情況下發生沖突,而在另一種情況下發生沖突,為什么? (下面的代碼)

[英]Java hashcodes collide in one case and not the other for the same objects, why? (Code Below)

我嘗試編寫一個小程序來演示僅覆蓋equals而不是hashcode()方法時Java中的哈希沖突。 這是為了證明兩個不相等的對象可以具有相同的哈希碼的理論。 這是針對詢問行為的面試問題。

我創建了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<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());

    }

}

注釋輸出:

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

當您不重寫hashCode() ,您將獲得從class Object繼承的標識哈希碼函數。

身份哈希碼取決於您看不到的東西,這些東西在理論上每次運行程序時都會發生變化,例如對象最終在內存中的位置,在您的對象之前創建的對象的數量等。您無法期望擁有程序或方法的不同運行之間身份哈希值的任何一致性。

但是,如果您兩次運行完全相同的程序,但又不太大,則很有可能兩次都將得到相同的哈希值。 但是,如果更改程序,則會更改通過加載和編譯該類將消耗的內存量,這很可能會通過更改對象將要進入的內存位置來更改標識哈希。

馬特·蒂默曼斯(Matt Timmermans)的回答很好地涵蓋了基本問題,尤其是“在不同的測試之間,您不能期望有任何一致性。” (1)

當前在Hotspot中,默認的Object.hashCode()實現(也稱為身份哈希碼,因為它與System.identityHashCode(obj)相同System.identityHashCode(obj)僅是一個帶有線程本地種子的偽隨機數。 相當長一段時間以來,對對象的內存地址沒有任何依賴性。 如果您的程序執行完全是確定性的,則哈希很可能是可重復的。

還要注意,身份哈希碼是在第一次調用Object.hashCode()System.identityHashCode()延遲生成的,並且該值存儲在該對象中,因此對該對象的后續調用將返回相同的值。 如果在另一個線程中運行沖突檢測器循環,則將獲得完全不同的哈希值,從而導致不同的沖突。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM