equals() и hashCode() в Java

191
15 сентября 2019, 10:50

Если мы переопределяем equals(), то обязаны переопределить hasCode(). Причем в обоих методах желательно использовать одни и те же поля класса. Причем, если equals() дает true, то и hashCode() должен быть одинаковым.

Возникает вопрос: а почему бы при переопределении equals() тогда просто не сравнивать hashCode()?

Answer 1

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

Но если equals для двух объектов возвращает true, то их hashCode будет одинаковый.

Answer 2

Алгоритм расчёта хэшкода в классе Object не учитывает содержимое класса и реализован так, что при каждом запуске программы, даже у экземпляра класса с одним и тем же содержимым хэш будет разным.

Поэтому, если Вы будете сравнивать экземпляры класса по хэшкоду без переопределения метода hashCode() у Вас всегда будет false.

Собственно, из-за этого и рекомендуется переопределять метод hashCode().

Метод equals() отвечает за определение эквивалентности объектов. Но, и он в классе Object не идеален, т.к. сравнивает лишь ссылки на объекты без учёта их содержимого. Потому его то же рекомендуют переопределять.

Как программист будет определять эквивалентность двух объектов с учётом их содержимого в equals() по большому счёту его личное дело. Только при вычислении хэшкода возможны, т.н. коллизии. Когда два разных объекта имеют одинаковый хэш. По этой причине одинаковые хэшкоды двух и более объектов, ещё не гарантия их эквивалентности. Поэтому при переопределении equals() просто сравнивать hashCode() нельзя.

Answer 3

Алгоритмы получения хеш не гарантируют уникальности получаемых значений для разных входных данных. Например слова "Siblings" и "Teheran" имеют идентичный хеш:

jshell> "Siblings".hashCode()
$7 ==> 231609873
jshell> "Teheran".hashCode()
$8 ==> 231609873
jshell> Objects.equals("Teheran", "Siblings")
$10 ==> false

Это пример коллизии хеш-функции, дело в том, что хеш-функция должна из любого входящего значения произвольной длинны вернуть число ограниченной длинны, в случае метода hashCode это 4-х байтовое целое число, которое может принимать одно из 2^32 возможных значений (это чуть больше четырех миллиардов).

Основным требованием к хэш-функции является равномерность распределения возвращаемых значений при случайном выборе аргументов. Но это не исключает возникновение коллизий. Например большинство сайтов не хранит в базе пароли пользователей в открытом виде, а только их хеш, полученный, например, посредством алгоритма md5. Этот алгоритм вернет 128-битное число, а это значит, что подобрать пароль будет возможно при переборе 2^128 возможных вариантов. Причем полученное, при переборе, входное значение не обязательно будет совпадать с фактическим паролем, но иметь точно такой же хеш.

Метод equals в java объектах как раз и нужен чтоб исключить влияние коллизий на нахождение элементов в коллекциях использующих hashCode и equals, это такие коллекции как HashMap и HashSet. Они используют метод hashCode для определения "места" где должен находится объект, в случае коллизии объекты имеющие идентичный хеш помещаются в список. Поиск среди таких объектов будет осуществляться перебором с вызовом метода equals для каждого элемента списка.

READ ALSO
Почему BroadcastReceiver не принимает сообщения?

Почему BroadcastReceiver не принимает сообщения?

Имеется часть кода, в которой создается интент:

143
Как аннотации (к примеру, @Retention) могут аннотировать сами себя?

Как аннотации (к примеру, @Retention) могут аннотировать сами себя?

Тут видно, что аннотация @Retention стоит собственно над объявлением самой аннотации @RetentionИ такой трюк можно делать не только с мета-аннотациями,...

162
проблема с Tomcat application context?

проблема с Tomcat application context?

Есть crud приложениеСервер томкэт деплоит его с адресом localhost:8080/GA_war_exploded/

150
Исправить ошибки, реализовать метод toString()

Исправить ошибки, реализовать метод toString()

Создать интерфейс IntList и его реализацию IntArrayList по аналогии с List<Integer>

151