Ответ
Для корректной работы объекта в качестве ключа HashMap между его методами hashCode() и equals() должен соблюдаться следующий контракт:
- Согласованность: Если
a.equals(b)возвращаетtrue, тоa.hashCode()должен быть равенb.hashCode(). - Обратное не обязательно: Равные хеш-коды (
a.hashCode() == b.hashCode()) не гарантируют, чтоa.equals(b) == true. Это коллизия. - Неизменность: Значения, возвращаемые
hashCode()и результатequals(), должны быть постоянными для объекта на протяжении его жизни (при условии, что поля, участвующие в сравнении, не изменяются).
Почему это важно для HashMap?
При добавлении элемента map.put(key, value):
- Вычисляется
hashCode()ключа для определения индекса корзины (bucket). - Если в корзине уже есть элементы (коллизия), для каждого из них вызывается
equals(key)для проверки, является ли ключ дубликатом.
Нарушение контракта приводит к ошибкам:
- Если
equalsговорит, что ключи одинаковы, ноhashCodeразный, дубликат ключа будет помещен в другую корзину, что нарушит уникальность ключей. - Объект, помещенный в мапу как ключ, после изменения своих полей может стать «ненаходимым», так как изменится его
hashCode().
Пример корректной реализации:
public class Person {
private final String id;
private String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id); // Сравниваем только по id
}
@Override
public int hashCode() {
return Objects.hash(id); // Хешируем только по id
}
}