Sobes.tech
Назад к вопросам
Middle — Middle+
52

Что произойдет, если класс, предназначенный для использования в качестве ключа в коллекции, имеет противоречащие определения методов equals и hashCode?

Компании, где спрашивали
Драйв Клик

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

Если equals() и hashCode() противоречат друг другу, коллекции на основе хеша начинают работать некорректно. Объекты, которые считаются равными, могут попадать в разные корзины, и их не удастся найти или удалить. Это ломает контракт Object и делает поведение HashMap, HashSet и похожих структур непредсказуемым.

Определение:

В Java контракт таков: если equals() возвращает true для двух объектов, то их hashCode() обязан быть одинаковым. Если это условие нарушить, хеш-коллекции не смогут корректно сопоставлять объекты при поиске, вставке и удалении. В результате в коллекции могут появляться дубликаты, а операции contains, get, remove будут давать неожиданный результат.

Пример использования:

Например, два объекта User считаются равными по id, но hashCode() вычисляется по name. Тогда одинаковые по смыслу пользователи могут попасть в разные buckets HashSet.

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

class User {
    private final int id;
    private final String name;

    User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return id == user.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name); // противоречит equals()
    }
}

public class Demo {
    public static void main(String[] args) {
        Set<User> set = new HashSet<>();
        User u1 = new User(1, "Alice");
        User u2 = new User(1, "Bob");

        set.add(u1);
        System.out.println(set.contains(u2)); // может быть false, хотя equals() == true
    }
}

Пояснение кода:

Код показывает нарушение контракта: equals() сравнивает только id, а hashCode() зависит от name.
Шаги такие:

  1. u1 добавляется в HashSet, и для него вычисляется хеш по name = Alice.
  2. При проверке contains(u2) хеш считается уже по name = Bob.
  3. Так как хеши разные, HashSet ищет объект в другой корзине и не находит его.
  4. Даже если equals() считает объекты равными, коллекция может вернуть false.

Ключевые моменты:

  • Если equals() возвращает true, hashCode() обязан быть одинаковым.
  • При нарушении контракта хеш-коллекции начинают вести себя некорректно.
  • Основные проблемы: невозможность найти, удалить или корректно сравнить элементы.
  • Особенно критично это для HashMap, HashSet, LinkedHashMap, LinkedHashSet.
  • При переопределении equals() и hashCode() их нужно проектировать вместе, от одних и тех же значимых полей.