Что произойдет при использовании класса в качестве ключа в HashMap/HashSet, если equals переопределен, а hashCode — нет?

«Что произойдет при использовании класса в качестве ключа в HashMap/HashSet, если equals переопределен, а hashCode — нет?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Это нарушит контракт между equals() и hashCode(), определенный в классе Object. Согласно контракту, если equals() возвращает true для двух объектов, то hashCode() должен возвращать для них одинаковое значение.

Последствия:

  • Объекты, равные по equals(), могут попасть в разные корзины (buckets) внутри HashMap или HashSet.
  • Это приведет к логическим ошибкам: структура будет содержать дубликаты ключей, а поиск и удаление элементов станут непредсказуемыми.

Пример некорректного класса:

class BadKey {
    private String id;
    // Конструктор, геттеры/сеттеры...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BadKey badKey = (BadKey) o;
        return Objects.equals(id, badKey.id);
    }
    // Метод hashCode() НЕ переопределен!
}

Демонстрация проблемы:

Map<BadKey, String> map = new HashMap<>();
BadKey key1 = new BadKey("a");
BadKey key2 = new BadKey("a"); // key1.equals(key2) == true

map.put(key1, "value1");
map.put(key2, "value2"); // Оба ключа добавятся, хотя ожидалось обновление значения
System.out.println(map.size()); // Выведет 2, а не 1
System.out.println(map.get(key1)); // "value1"
System.out.println(map.get(key2)); // "value2" — значение по key1 не найдено!

Решение: Всегда переопределяйте hashCode(), когда переопределяете equals(). Используйте Objects.hash() для удобной реализации.