2017-01-13 7 views
2

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

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

Однако я получаю очень удивительное поведение. Если я перебирать вложенную цикл и напечатать первую столкновение hashcodes, я получаю такое же значение Hashcode

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()); 

    } 

} 

Comemented Выход:

employee_121949 
employee_196 
1829164700 
366712642 
1829164700 
366712642 

Un комментируются Выходной контур

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 
+0

Это (относительно) отлично подходит для двух неравных объектов, имеющих одинаковый хэш-код. Проблема в том, что два одинаковых объекта имеют разные хэш-коды. В этом случае hashmaps просто не будут работать. Если вы не переопределите hashcode, вы не получите согласованные хэш-коды на своих объектах. – khelwood

+2

Итак, вы используете два разных бинарных файла и задаетесь вопросом, почему они ведут себя по-другому? Объекты не имеют одинаковых хэш-кодов всегда в 196-м и 121949-м объектах, созданных в массиве. Это не так, как hashCode() работает. –

+0

Обратите внимание, что проблема, вызванная переопределением 'equals', а не' hashCode', не является столкновением. Ожидается, что коды хэша столкнутся. Проблема в том, что у вас могут быть * одинаковые * объекты с * разными хэш-кодами, что означает, что вы не найдете их в HashMaps и т. Д. –

ответ

1

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

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

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

4

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

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

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

+0

Есть ли источник этого, насколько я знаю, по принципу «День рождения», если я создаю объекты на 200 000 К, я должен получить столкновение, как итерация по каждому индексу меняет что-нибудь? – Sameer

+1

Я не сказал, что вы не столкнетесь. Я сказал, что хэш-коды будут разными. Это означает, что вы столкнетесь с * разными * столкновениями. Кроме того, это не повторение кодов, которые действительно имеют значение, это добавление кода для выполнения итерации. –

+0

Я попытался добавить еще один массив размером 200000, чтобы увидеть, не изменились ли конфликтующие индексы, они этого не сделали, поэтому, по-видимому, внесение изменений в двоичный код с циклом uncommitted не вносит никаких изменений. Таким образом, гипотеза о том, что изменение двоичных изменений имеет hashcode dosent hold. – Sameer