Как работает hashCode?
Метод hashCode() в Java — это часть контракта между объектами и хэш-структурами, такими как HashMap, HashSet, Hashtable и другими. Он возвращает целое число (int), которое используется как хэш-код объекта, то есть его числовое представление.
Этот хэш-код помогает быстро найти объект в коллекции или проверить, равны ли объекты, не сравнивая их полностью.
📌 Основная идея
Метод hashCode() реализует хэш-функцию — она преобразует внутреннее состояние объекта (например, поля) в int. При этом:
-
Объекты с одинаковыми значениями (согласно equals()) должны возвращать одинаковые hashCode().
-
Объекты с разными значениями могут возвращать одинаковый hashCode() (это называется коллизией, и она допустима, но нежелательна).
Контракт между equals() и hashCode()
-
Если a.equals(b) == true, то a.hashCode() == b.hashCode() обязательно.
-
Если a.hashCode() != b.hashCode(), то a.equals(b) точно false.
-
Если a.hashCode() == b.hashCode(), это не гарантирует, что a.equals(b) == true.
Стандартная реализация (по умолчанию)
В классе Object метод hashCode() возвращает уникальный код, основанный на адресе объекта в памяти. Поэтому:
Object a = new Object();
Object b = new Object();
System.out.println(a.hashCode()); // например, 1024872
System.out.println(b.hashCode()); // другой код
→ Хотя два объекта a и b равны по типу, они имеют разные хэш-коды, потому что ссылаются на разные объекты.
✍️ Как правильно переопределить hashCode()
Если вы переопределяете equals(), вы обязаны переопределить и hashCode().
Пример:
public class Person {
String name;
int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person p = (Person) o;
return age == p.age && Objects.equals(name, p.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Метод Objects.hash(...) из Java 7+ удобно создаёт хэш-код, основываясь на переданных полях.
🔁 Пример использования в HashMap
Map<Person, String> map = new HashMap<>();
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
map.put(p1, "Engineer");
System.out.println(map.get(p2)); // вернёт "Engineer" только если hashCode и equals переопределены правильно
Если hashCode() не переопределён, p2 попадёт в другую ячейку и get() вернёт null.
Принцип работы в HashMap
-
При добавлении map.put(key, value):
-
Вызывается key.hashCode() → получает хэш.
-
Хэш преобразуется в индекс массива (bucket).
-
Внутри "корзины" сравниваются объекты через equals().
-
-
При поиске map.get(key):
-
Опять вызывается hashCode() → определяет, в какой "корзине" искать.
-
Затем внутри сравниваются ключи по equals().
-
Если hashCode() не совпадает, сравнение по equals() не будет даже производиться.
⚠️ Распространённые ошибки
-
Не переопределён hashCode() при наличии equals() → HashSet и HashMap не работают корректно.
-
Использование нестабильных полей (например, изменяемых после вставки в коллекцию) → хэш меняется, и объект становится "невидимым" в хэш-таблице.
-
Нарушение контракта между equals() и hashCode().
🧠 Итого
Метод hashCode():
-
Возвращает int, отражающий "содержание" объекта.
-
Используется для быстрой индексации в хэш-коллекциях (HashMap, HashSet).
-
Должен быть согласован с equals() — объекты, равные по equals(), обязаны иметь одинаковый hashCode().
Правильная реализация hashCode() — ключевой момент для корректной работы коллекций и производительности.