Java hashcodes сталкиваются в одном случае, а не в другом для тех же объектов, почему? (Код ниже)

Я попытался написать небольшую программу для демонстрации хеш-коллизий в java, когда только equals переопределен, а не hashcode(). Это должно было доказать теорию, что два неравных объекта могут иметь один и тот же хэш-код. Это было для вопроса Интервью, где было задано поведение.

Я создал 200 000 объектов, сохранил их в массиве, а затем сравнил их, чтобы увидеть, какие дубликаты. (Для этого я использую вложенный цикл цикла, итерации по массиву объектов после фазы создания объекта.) Для около 200 000 объектов я получаю 9 коллизий. Первый из них является объектом с индексами 196 и 121949. Затем я продолжаю печатать эти хэш-коды, чтобы показать, что оба значения одинаковы.

Однако я получаю очень удивительное поведение. Если я перебираю цикл вложенных циклов и печатаю первое столкновение хэш-кодов, я получаю одинаковое значение хэш-кода

1867750575
1867750575 

для обоих объектов в индексах 196 и 121949.

Но если я прокомментирую цикл вложенных циклов для обнаружения всех столкновений и напрямую печатаю хэш-код для элементов в индексах 196 и 121949, я получаю

1829164700
366712642

Обратите внимание: я не комментирую создание этих элементов, а только ту часть, где я проверяю наличие конфликтов.

Почему это происходит, даже если я не повторяю их, не должен ли хэш-код быть последовательным?

Добавление 1: Есть ли источник позади этого, насколько мне известно, по принципу "День рождения", если я создаю 200 000 объектов, я должен получить столкновение, как происходит итерация по каждому индексу или что-то не изменяет?

Добавление 2: я попытался добавить еще один массив размером 200000, чтобы увидеть, не изменились ли сменяющиеся индексы, они не сделали этого, поэтому, по-видимому, внесение изменений в двоичный код с помощью цикла uncommitted не вносит никаких изменений. Таким образом, гипотеза о том, что изменение бинарных изменений hashcode не выполняется.

Вот мой код

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

Ответы

Ответ 1

Ответ Мэтта Тиммерманса охватывает основные проблемы достаточно хорошо, в частности "Вы не можете рассчитывать на согласованность... между разными тиражами". (+1)

Реализация по умолчанию Object.hashCode() (также называемая хэш-кодом идентификатора, так как она совпадает с System.identityHashCode(obj)) в настоящее время, в Hotspot, только псевдослучайное число с локальным именем потока. В течение некоторого времени не было никакой зависимости от адреса памяти объекта. Если выполнение вашей программы полностью детерминировано, хеши, скорее всего, будут повторяемы.

Также обратите внимание, что идентификатор hashcode лениво генерируется при первом вызове Object.hashCode() или System.identityHashCode(), и значение сохраняется в объекте, чтобы последующие вызовы этого объекта возвращали одно и то же значение. Если вы запустили цикл обнаружения столкновений в другом потоке, вы получите совершенно разные значения хэша и, следовательно, различные столкновения.

Ответ 2

Если вы не переопределяете hashCode(), вы получаете функцию хеш-кода идентификатора, унаследованную от class Object.

Идентификационный код хеша зависит от того, что вы не видите, которое теоретически может измениться каждый раз, когда вы запускаете свою программу, например, когда объект заканчивается в памяти, количество объектов, созданных до вас, и т.д. Вы просто не можете ожидайте, что будет какая-либо согласованность в значениях хеш-идентификаторов между различными прогонами вашей программы или метода.

Однако, если вы запускаете ровно одну и ту же программу дважды, и она не слишком велика, шансы очень хороши, что в итоге вы получите одинаковые хеши. Однако, если вы меняете программу, вы меняете, сколько памяти будет потребляться, загружая и компилируя класс, что очень вероятно изменит хэши идентификации, изменив местоположение в памяти, в которое будут идти ваши объекты.