Тема equals() и hashCode() уже настолько заезжена, что почти любой разработчик без запинки скажет:



"Если переопределил equals(), нужно переопределить и hashCode()."

Но на собеседованиях обычно валятся не на этом. Валятся на понимании почему. Например, что выведет этот код?

class Person {
private String name;

// equals() и hashCode() используют поле name
}

Person p = new Person("Andy");

Set<Person> set = new HashSet<>();

set.add(p);

p.setName("John");

System.out.println(set.contains(p));

Большинство отвечает:
true

Ведь мы ищем тот же самый объект. Но правильный ответ — false.

Почему?

Когда объект попадает в HashSet, сначала вычисляется его hashCode(). На основании этого значения определяется bucket, в который объект будет помещён. После того как мы изменили name, изменился и hashCode(). Теперь contains() вычисляет уже новый hash и ищет объект совсем в другом bucket.
Самое забавное, что объект никуда не делся — он всё ещё лежит внутри HashSet. Просто коллекция больше не может его найти.
Именно поэтому поля, участвующие в equals() и hashCode(), должны быть неизменяемыми. Или как минимум не должны изменяться после помещения объекта в HashMap или HashSet.

Есть ещё одна классическая ловушка. Некоторые переопределяют только equals():
@Override
public boolean equals(Object o) {
...
}

и забывают про hashCode().

В результате:
Person p1 = new Person("Andy");
Person p2 = new Person("Andy");

System.out.println(p1.equals(p2)); // true

Set<Person> set = new HashSet<>();
set.add(p1);

System.out.println(set.contains(p2)); // false

Почему?

Потому что HashSet сначала ищет объект по hashCode(). Если hash отличается, до вызова equals() дело вообще может не дойти.

Отсюда и контракт:
- если equals() возвращает true, то hashCode() обязан быть одинаковым;
- одинаковый hashCode() не означает, что объекты равны — в одном bucket могут лежать разные объекты, и тогда уже вызывается equals().

Именно это, кстати, часто забывают. Многие думают, что hashCode() должен быть уникальным.
Нет.
Он нужен не для определения равенства, а для быстрого поиска нужного bucket. Финальное решение о равенстве почти всегда принимает именно equals().
Вот это уже тот момент, который действительно важно понимать.

Потому что вопрос на собеседовании обычно звучит не «что такое equals() и hashCode()», а «почему HashSet перестал находить объект, который в нём лежит?».
Просмотры: 90
Открыть в MAX Сообщить о неточности

Репосты публикации

Публикация канала

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти