2009-04-13 2 views
39

Это странно. Сотрудник спросил о реализации myArray.hashCode() в java. Я думал, что знаю, но потом я провел несколько тестов. Проверьте код ниже. Странное мнение, что я заметил, это то, что когда я написал первый синус, результаты были разными. Обратите внимание, что это почти так, как будто он сообщает адрес памяти, и изменение класса переместило адрес или что-то в этом роде. Просто подумал, что я поделюсь.Реализация Java Array HashCode

int[] foo = new int[100000]; 
java.util.Random rand = new java.util.Random(); 

for(int a = 0; a < foo.length; a++) foo[a] = rand.nextInt(); 

int[] bar = new int[100000]; 
int[] baz = new int[100000]; 
int[] bax = new int[100000]; 
for(int a = 0; a < foo.length; a++) bar[a] = baz[a] = bax[a] = foo[a]; 

System.out.println(foo.hashCode() + " ----- " + bar.hashCode() + " ----- " + baz.hashCode() + " ----- " + bax.hashCode()); 

// returns 4097744 ----- 328041 ----- 2083945 ----- 2438296 
// Consistently unless you modify the class. Very weird 
// Before adding the comments below it returned this: 
// 4177328 ----- 4097744 ----- 328041 ----- 2083945 


System.out.println("Equal ?? " + 
    (java.util.Arrays.equals(foo, bar) && java.util.Arrays.equals(bar, baz) && 
    java.util.Arrays.equals(baz, bax) && java.util.Arrays.equals(foo, bax))); 

ответ

77

java.lang.ArrayhashCode метод унаследован от Object, что означает, что хэш-код зависит от ссылки. Чтобы получить хэш-код на основе содержимого массива, используйте Arrays.hashCode.

Остерегайтесь его реализации мелкого хэш-кода. Также имеется глубокая реализация Arrays.deepHashCode.

+1

Спасибо за этот ответ, но почему java.lang.Array по умолчанию не переопределяет методы hashCode (и toString)? Есть ли веская причина? –

+4

Поскольку hashCode должен быть быстрым, чтобы быть полезным (поскольку он в основном используется для предотвращения дорогостоящего вызова .equals), и даже hashCode на мелком значении массива может потенциально быть очень медленным. Хэш-код, который в основном случайный, не болит, он просто не дает никакого преимущества. Меньше двух зол. – Torque

4

Массивы использовать хэш-код по умолчанию в, который основан на ячейку памяти (но это не обязательно место памяти, так как это только int и все адреса памяти не поместится). Вы можете увидеть это, также распечатав результат System.identityHashCode(foo).

Массивы - это только equal, если они одинаковые, идентичные. Таким образом, хэш-коды массива будут равны, как правило, если они являются одинаковыми, идентичными массивами.

+0

(и объекты перемещаются в памяти, и если вы посмотрите на хэш-коды, они обычно не похожи на адреса) –

2

Реализация по умолчанию для Object.hashCode() действительно возвращает значение указателя объекта, хотя это зависит от реализации. Например, 64-разрядная JVM может принимать указатель и XOR, а слова высокого и низкого порядка вместе. Подклассам рекомендуется переопределять это поведение, если это имеет смысл.

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

Обратите внимание, что java.util.Arrays предоставляет реализацию deepHashCode(), когда важно, чтобы хэширование на основе содержимого массива, а не идентификатор самого массива.

+1

Современные виртуальные машины перемещают объекты в памяти. Текущий адрес может использоваться как семя, но результат должен быть сохранен. –

+1

Перемещение в памяти по-прежнему не приводит к изменению хеш-кода. –

2

Я согласен с использованием java.util.Arrays.hashCode (или Google гуавы родовое обертку Objects.hashcode), но следует помнить, что это может вызвать проблемы, если вы используете Terracotta - см this link

Смежные вопросы