Что такое хэш-код объекта, если hashCode() не переопределен?
Если метод hashCode() не переопределен, что будет результатом вызова hashCode() для любого объекта в Java?
Ответы
Ответ 1
Как правило, hashCode() просто возвращает адрес объекта в памяти, если вы не переопределяете его.
Из 1:
Насколько разумно практично, метод hashCode, определенный классом Object, возвращает разные целые числа для разных объектов. (Обычно это выполняется путем преобразования внутреннего адреса объекта в целое число, но этот способ реализации не требуется языком программирования JavaTM.)
Ответ 2
В HotSpot JVM по умолчанию при первом вызове неперегруженных Object.hashCode
или System.identityHashCode
случайное число генерируется и сохраняется в заголовке объекта. Последующие вызовы Object.hashCode
или System.identityHashCode
просто извлекают это значение из заголовка. По умолчанию он не имеет ничего общего с содержимым объекта или местоположением объекта, просто случайным числом. Это поведение контролируется опцией -XX:hashCode=n
HotSpot JVM, которая имеет следующие возможные значения:
- 0: используйте глобальный случайный генератор. Это значение по умолчанию в Java 7. Недостаток заключается в том, что одновременные вызовы из нескольких потоков могут вызывать состояние гонки, что приведет к генерации одного и того же хэш-кода для разных объектов. Кроме того, в высококонкурентной среде задержки возможны из-за конкуренции (с использованием той же области памяти из разных ядер процессора).
- 5: используйте некоторый поток-локальный случайный генератор xor-shift, который свободен от предыдущих недостатков. Это значение по умолчанию в Java 8.
- 1: используйте указатель на объект, смешанный с некоторым случайным значением, которое изменяется на событиях "остановить-мир", поэтому между событиями stop-the world (например, сбор мусора) сгенерированные хэш-коды являются стабильными (для целей тестирования/отладки )
- 2: всегда использовать
1
(для тестирования/отладки)
- 3: используйте автоинкрементные номера (для целей тестирования/отладки, также используется глобальный счетчик, таким образом возможны конфликты и условия гонки).
- 4: при необходимости используйте указатель объекта до 32 бит (для тестирования/отладки)
Обратите внимание, что даже если вы установите -XX:hashCode=4
, hashCode не всегда укажет на адрес объекта. Объект может быть перемещен позже, но hashCode останется прежним. Кроме того, адреса объектов плохо распределены (если в вашем приложении используется не так много памяти, большинство объектов будут расположены близко друг к другу), поэтому вы можете иметь неуравновешенные хеш-таблицы, если вы используете эту опцию.
Ответ 3
Реализация hashCode()
может отличаться от класса к классу, но контракт для hashCode()
является очень конкретным и четко и четко изложено в Javadocs:
Возвращает значение хэш-кода для объекта. Этот метод поддерживается в интересах хэш-таблиц, таких как предоставленные java.util.Hashtable.
Общий контракт hashCode:
- Всякий раз, когда он вызывается одним и тем же объектом более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число, если информация, используемая при равных сравнениях с объектом, не изменяется. Это целое число не должно оставаться согласованным с одним исполнением приложения на другое выполнение одного и того же приложения.
- Если два объекта равны в соответствии с методом equals (Object), то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат.
- Не требуется, чтобы, если два объекта неравны в соответствии с методом equals (java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен производить различные целочисленные результаты. Тем не менее, программист должен знать, что получение отдельных целочисленных результатов для неравных объектов может улучшить производительность хеш-таблиц.
Насколько разумно практично, метод hashCode, определенный классом Object, возвращает разные целые числа для разных объектов. (Обычно это выполняется путем преобразования внутреннего адреса объекта в целое число, но этот способ реализации не требуется языком программирования JavaTM.)
hashCode()
тесно привязан к equals()
, и если вы переопределите equals()
, вы также должны переопределить hashCode()
.
Ответ 4
Если hashcode не переопределяется, вы вызываете Object hashcode, вот выдержка из его javadoc:
Насколько разумно практично, метод hashCode, определенный классом Object, возвращает разные целые числа для разных объектов. (Обычно это выполняется путем преобразования внутреннего адреса объекта в целое число, но этот способ реализации не требуется языком программирования JavaTM.)
Ответ 5
реализация hashcode по умолчанию дает внутренний адрес объекта в jvm как 32-битное целое число. Таким образом, два разных объекта (в памяти) будут иметь разные хэш-коды.
Это согласуется с реализацией по умолчанию равных. Если вы хотите переопределить равные для ваших объектов, вам придется адаптировать hashCode так, чтобы они были согласованы.
Для хорошего обзора см. http://www.ibm.com/developerworks/java/library/j-jtp05273.html.
Ответ 6
Вам следует попытаться реализовать хеш-код, чтобы разные объекты дали разные результаты. Я не думаю, что есть стандартный способ сделать это.
Прочитайте эту статью для информации.
Ответ 7
Хэш-код полезен для хранения объекта в коллекции, например хешета. Позволяя объекту определять Hashcode как нечто уникальное, он позволяет эффективно работать с алгоритмом HashSet.
Сам объект использует адрес Object в памяти, который является очень уникальным, но может быть не очень полезным, если два разных объекта (например, две одинаковые строки) должны рассматриваться одинаково, даже если они дублируются в памяти.
Ответ 8
Два объекта с другим хеш-кодом не должны быть равны по отношению к equals()
a.hashCode() != b.hashCode()
должно означать !a.equals(b)
Однако два объекта, которые не равны по отношению к equals(), могут иметь один и тот же хэш-код. Хранение этих объектов в наборе или карте станет менее эффективным, если многие объекты имеют один и тот же хэш-код.
Ответ 9
Не совсем ответ, но добавление к моему предыдущему комментарию
внутренний адрес объекта не может оставаться неизменным в JVM, чей сборщик мусора может перемещать его во время уплотнения кучи.
Я попытался сделать что-то вроде этого:
public static void main(String[] args) {
final Object object = new Object();
while (true) {
int hash = object.hashCode();
int x = 0;
Runtime r = Runtime.getRuntime();
List<Object> list = new LinkedList<Object>();
while (r.freeMemory() / (double) r.totalMemory() > 0.3) {
Object p = new Object();
list.add(p);
x += object.hashCode();//ensure optimizer or JIT won't remove this
}
System.out.println(x);
list.clear();
r.gc();
if (object.hashCode() != hash) {
System.out.println("Voila!");
break;
}
}
}
Но хэш-код действительно не меняется... может ли кто-нибудь сказать мне, как Sun JDK реализует Obect.hashcode?
Ответ 10
возвращает шестизначный шестнадцатеричный номер. Обычно это место памяти слота, в котором объект адресован. Из алгоритма per se, я думаю, JDK делает двойное хэширование (собственная реализация), которое является одной из лучших функций хэширования для открытой адресации. Эта схема двойного хэширования значительно снижает вероятность столкновений.
Следующий пост даст поддержку -
Java - путаница HashMap о обработке столкновений и методе get()
Ответ 11
Вы должны переопределить hashCode в каждом классе, который переопределяет равные. Невыполнение этого правила приведет к нарушению общего контракта для Object.hashCode, который предотвратит правильное функционирование вашего класса в conjunction with all hash-based collection
s, including HashMap, HashSet, and Hashtable.