Ответ
Для корректной работы объекта в качестве ключа 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
}
} Ответ 18+ 🔞
А, ну это же классика, блядь! Сейчас объясню, как эти два закадычных метода в HashMap друг за друга горой стоят, а если один подведёт — пиши пропало, всё наебнётся.
Представь себе, hashCode() и equals() — это как два мудака на стройке. Один (hashCode) кричит: «Эй, тащите этот ящик на третий этаж, в секцию «Б»!». А второй (equals) уже там, на третьем этаже, встречает и проверяет: «Так, стоп, а этот ящик точно такой же, как тот, что уже лежит? Или другой?».
И вот их священные уговоры, контракт, блядь:
-
Главное правило, ебать его в сраку: Если
equalsсказал, что два объекта — одно и то же говно (a.equals(b) == true), то иhashCodeу них должен быть одинаковый, как сиамские близнецы. ИначеHashMapпросто обосрётся и положит «одинаковый» ключ в другую корзину, и ты его потом никогда не найдёшь. Полный пиздец с уникальностью. -
А вот наоборот — не факт, блядь. Если хеш-коды совпали (
a.hashCode() == b.hashCode()), это ещё не значит, что объекты равны. Это просто коллизия, два разных ящика в одну секцию засунули. Ну бывает, ёпта. Тогда ужеequalsпридёт и разберётся, кто есть кто. -
И главное — не шевелись, сука! Пока объект лежит в мапе ключом, его
hashCode()и результатequals()должны быть как скала — неизменны. А то представь: положил ты объект, всё хорошо. Потом ты ему поля поменял, хеш-код уехал в другую вселенную. Ты его потом искать будешь по старому хешу — а его там уже нет! Объект как в воду канул, хотя мапа трясёшь — он там где-то есть. Волшебство, блядь, да и только.
Короче, если нарушить этот контракт, HashMap превратится из умной структуры в кучу беспорядочного мусора, который ничего не находит и всё дублирует. Хуйня полная.
Вот смотри, как надо делать по-человечески, чтобы не было потом мучительно больно:
public class Person {
private final String id; // Вот это поле — наше всё, по нему и сравниваем, и хешируем
private String name; // А это можно менять, оно в equals/hashCode не участвует
public Person(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // Один и тот же объект? Ну ясень пень, 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 же! Красота, один хуй в пальто.
}
}
Вот так, сука, и живём. Согласованно. А то накосячишь с этими методами — потом дебажи, ищи ветра в поле.