Какой контракт между equals и hashCode
Что проверяют
Знаете ли вы правила и понимаете ли, что ломается при их нарушении. Вопрос базовый, но отсеивает многих.
Как отвечать
Контракт три пункта:
- равные по
equalsобъекты обязаны возвращать одинаковыйhashCode; - разные объекты могут иметь одинаковый хеш, это коллизия и она допустима;
- пока объект не изменился,
hashCodeобязан возвращать одно и то же значение.
Сам equals должен быть рефлексивным, симметричным, транзитивным, устойчивым и давать false для null.
Что ломается при нарушении. Переопределили equals и забыли hashCode: два равных объекта попадают в разные корзины, и объект, положенный в HashMap или HashSet, перестаёт находиться. Ошибка тихая, компилятор молчит.
Второй случай: изменили поле, участвующее в хеше, у ключа, уже лежащего в карте. Объект остаётся в старой корзине, поиск идёт в новую, значение недостижимо и не удаляется. Отсюда правило брать в ключи неизменяемые типы.
Практика: считать оба метода по одному и тому же набору полей, использовать Objects.equals и Objects.hash, а лучше record, который генерирует их сам по всем компонентам. В Kotlin ту же роль играет data class.
Где спотыкаются
Считают, что одинаковый хеш означает равенство.
Переопределяют только один из методов.
Берут в equals изменяемые поля у объекта, который кладут в множество.
Пишут hashCode, возвращающий константу: контракт формально не нарушен, а карта вырождается в список.