Связь между hashCode и методом equals в Java

Я читаю во многих местах, говоря при переопределении equals метод в Java, должен переопределить hashCode метод тоже, иначе он "нарушает договор".

Но пока я не столкнулся с какой-либо проблемой, если переопределил только метод equals, но не метод hashCode.

Что такое контракт? И почему я не сталкиваюсь с какой-либо проблемой, когда нарушаю договор? В каком случае я столкнусь с проблемой, если я не переопределил метод hashCode?

7 ответов

Решение

Проблема у вас будет с коллекциями, где уникальность элементов рассчитывается в соответствии с обоими .equals() а также .hashCode() например ключи в HashMap,

Как следует из его названия, он опирается на хеш-таблицы, а хеш-сегменты являются функцией объекта .hashCode(),

Если у вас есть два объекта, которые .equals(), но есть разные хэш-коды, вы потеряете!

Важной частью договора здесь являются: объекты, которые .equals() ДОЛЖЕН иметь то же самое .hashCode(),

Это все задокументировано в Javadoc для Object, И Джошуа Блох говорит, что вы должны делать это в эффективной Java. Достаточно сказано.

Согласно документу, реализация по умолчанию hashCode вернет некоторое целое число, которое отличается для каждого объекта

Насколько это практически целесообразно, метод hashCode, определенный классом Object, возвращает разные целые числа для разных объектов. (Обычно это реализуется путем преобразования внутреннего адреса объекта в целое число, но эта реализация
техника не требуется языком программирования JavaTM.)

Однако когда-нибудь вы захотите, чтобы хеш-код был одинаковым для разных объектов, имеющих одинаковое значение. Например

Student s1 = new Student("John", 18);
Student s2 = new Student("John", 18);
s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode

Такая проблема будет возникать, если вы используете хеш-структуру данных в структуре сбора, такой как HashTable, HashSet. Особенно с такой коллекцией, как HashSet, вы в конечном итоге получите дубликат элемента и нарушите контракт Set.

Да, это должно быть отменено. Если вы думаете, что вам нужно переопределить equals() тогда нужно переопределить hashCode() и наоборот. Общий контракт hashCode():

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

  2. Если два объекта равны в соответствии с методом equals(Object), то вызов метода hashCode для каждого из двух объектов должен привести к одному и тому же целочисленному результату.

  3. Не требуется, чтобы, если два объекта были неравны в соответствии с методом equals(java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен приводить к разным целочисленным результатам. Тем не менее, программист должен знать, что выдача различных целочисленных результатов для неравных объектов может повысить производительность хеш-таблиц.

Посмотри на Hashtables, Hashmaps, HashSets и так далее. Все они хранят хешированный ключ как свои ключи. При вызове get(Object key) хеш параметра генерируется и ищется в данных хешах.

Когда не перезаписать hashCode() и экземпляр ключа был изменен (например, простая строка, которая не имеет никакого значения), hashCode() может привести к 2 различным хеш-кодам для одного и того же объекта, что приведет к тому, что ваш ключ не будет найден в map.get(),

Контракт заключается в том, что если obj1.equals(obj2) затем obj1.hashCode() == obj2.hashCode(), это в основном по соображениям производительности, так как карты в основном используют метод hashCode для сравнения ключей ввода.

Смотрите JavaDoc из java.lang.Object

В hashCode() это говорит:

Если два объекта равны согласно equals(Object) метод, затем вызывая hashCode Метод на каждом из двух объектов должен давать одинаковый целочисленный результат.

(Акцент мной).

Если вы только переопределите equals() и не hashCode() ваш класс нарушает этот контракт.

Это также сказано в JavaDoc equals() метод:

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

Контракт: если два объекта равны, то они должны иметь одинаковый хеш-код, а если два объекта не равны, то они могут иметь или не иметь одинаковый хэш-код.

Попробуйте использовать свой объект в качестве ключа в HashMap (отредактировано после комментария от joachim-sauer), и вы начнете сталкиваться с проблемами. Контракт - это руководство, а не что-то навязанное вам.

Другие вопросы по тегам