Как работает hashCode?

Метод hashCode() в Java — это часть контракта между объектами и хэш-структурами, такими как HashMap, HashSet, Hashtable и другими. Он возвращает целое число (int), которое используется как хэш-код объекта, то есть его числовое представление.

Этот хэш-код помогает быстро найти объект в коллекции или проверить, равны ли объекты, не сравнивая их полностью.

📌 Основная идея

Метод hashCode() реализует хэш-функцию — она преобразует внутреннее состояние объекта (например, поля) в int. При этом:

  • Объекты с одинаковыми значениями (согласно equals()) должны возвращать одинаковые hashCode().

  • Объекты с разными значениями могут возвращать одинаковый hashCode() (это называется коллизией, и она допустима, но нежелательна).

Контракт между equals() и hashCode()

  1. Если a.equals(b) == true, то a.hashCode() == b.hashCode() обязательно.

  2. Если a.hashCode() != b.hashCode(), то a.equals(b) точно false.

  3. Если 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&lt;Person, String&gt; 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

  1. При добавлении map.put(key, value):

    • Вызывается key.hashCode() → получает хэш.

    • Хэш преобразуется в индекс массива (bucket).

    • Внутри "корзины" сравниваются объекты через equals().

  2. При поиске map.get(key):

    • Опять вызывается hashCode() → определяет, в какой "корзине" искать.

    • Затем внутри сравниваются ключи по equals().

Если hashCode() не совпадает, сравнение по equals() не будет даже производиться.

⚠️ Распространённые ошибки

  • Не переопределён hashCode() при наличии equals() → HashSet и HashMap не работают корректно.

  • Использование нестабильных полей (например, изменяемых после вставки в коллекцию) → хэш меняется, и объект становится "невидимым" в хэш-таблице.

  • Нарушение контракта между equals() и hashCode().

🧠 Итого

Метод hashCode():

  • Возвращает int, отражающий "содержание" объекта.

  • Используется для быстрой индексации в хэш-коллекциях (HashMap, HashSet).

  • Должен быть согласован с equals() — объекты, равные по equals(), обязаны иметь одинаковый hashCode().

Правильная реализация hashCode() — ключевой момент для корректной работы коллекций и производительности.