Когда в Java необходимо переопределять методы equals() и hashCode()?

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

Ответ

Методы equals() и hashCode() обязательно нужно переопределять вместе, когда:

  1. Вы планируете использовать объекты этого класса как ключи в HashMap, HashSet, Hashtable или любой другой hash-базированной коллекции.
  2. Логическое равенство объектов отличается от равенства по ссылкам (==). Например, для класса Person два объекта с одинаковыми именем и паспортными данными должны считаться равными, даже если это разные экземпляры в памяти.

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

  • Если два объекта равны по equals(), их hashCode() обязаны быть одинаковыми.
  • Обратное не обязательно: одинаковый hashCode() не гарантирует равенства объектов (коллизия).

Пример корректной реализации:

public class Person {
    private final String name;
    private final String passportId;

    public Person(String name, String passportId) {
        this.name = name;
        this.passportId = passportId;
    }

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

    @Override
    public int hashCode() {
        // Используем готовый метод для вычисления хэша по нескольким полям
        return Objects.hash(name, passportId);
    }
}

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