← Назад к вопросам

Что такое контракт между equals и hashCode?

2.0 Middle🔥 111 комментариев
#Kotlin основы#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Контракт между equals() и hashCode() — это набор взаимосвязанных правил, определённых в Java-документации классов Object и Object.hashCode(), которые гарантируют корректную работу объектов в хеш-ориентированных коллекциях, таких как HashMap, HashSet и Hashtable. Нарушение этого контракта приводит к непредсказуемому поведению этих коллекций.

Основные правила контракта

  1. Согласованность при равенстве:
    Если два объекта равны согласно методу equals(), то их хеш-коды должны быть одинаковыми.

    if (obj1.equals(obj2)) {
        // Тогда обязательно должно выполняться:
        // obj1.hashCode() == obj2.hashCode()
    }
    
  2. Согласованность во времени:
    Хеш-код объекта должен оставаться постоянным на протяжении всего времени выполнения приложения, при условии, что объект не изменяется (если объект immutable, то хеш-код никогда не меняется).

  3. Обратное необязательно:
    Если хеш-коды двух объектов совпадают, это не гарантирует их равенства через equals(). Коллизии хеш-кодов разрешаются внутри коллекций.

Почему этот контракт критически важен?

Хеш-коллекции используют хеш-код для быстрого поиска объектов. Например, HashMap сначала вычисляет хеш-код ключа, чтобы определить корзину (bucket), где хранится пара ключ-значение. Затем внутри корзины используется equals() для точного сравнения.

// Пример нарушения контракта
public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    // Если hashCode() не переопределён — нарушение контракта!
}

Если переопределить только equals() (как выше), то два логически равных объекта могут иметь разные хеш-коды (унаследованные от Object.hashCode()). Тогда при добавлении в HashSet:

Set<Person> set = new HashSet<>();
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);

System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // false — нарушение!

set.add(p1);
set.add(p2);
// В множестве окажутся ОБА объекта, хотя должны быть одним

Правильная реализация

Используйте общие поля для вычисления hashCode() и equals():

public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age); // Согласованность гарантирована
    }
}

Последствия нарушения контракта

  • Потеря объектов в HashMap/HashSet: объект может быть не найден по ключу, даже если логически равный ключ присутствует.
  • Дубликаты в HashSet: множество может содержать одинаковые объекты.
  • Нестабильная работа коллекций: производительность деградирует до O(n), так как все объекты попадают в одну корзину или не находятся.

Итог

Контракт между equals() и hashCode() — это фундаментальное правило Java, обеспечивающее:

  • Корректность хеш-коллекций.
  • Эффективность поиска (близкую к O(1)).
  • Предсказуемость поведения программы.

Всегда переопределяйте оба метода вместе, используя одни и те же поля, и применяйте вспомогательные методы Objects.equals() и Objects.hash() для надёжной реализации.

Что такое контракт между equals и hashCode? | PrepBro