Что будет с программой, если hashCode и equals переопределены корректно
Что проверяют
Вопрос с подвохом. Прямой ответ короткий, а интервьюер ждёт, что вы сами назовёте случаи, где корректные по контракту методы всё равно приводят к багам. Следом обычно спрашивают, что будет, если переопределить только equals, и что случится с ключом HashMap, у которого поменяли поле.
Как отвечать
Сначала прямой ответ: хеш-коллекции (HashMap, HashSet, ConcurrentHashMap) работают как задумано. Повторный put с равным ключом заменяет значение, contains находит логически равный объект, дубликатов нет. Контракт: equals рефлексивен, симметричен, транзитивен, консистентен и возвращает false для null; равные объекты обязаны иметь равный hashCode, обратное не требуется.
Затем оговорки, ради которых вопрос и задают:
- изменяемый ключ: если после вставки поменять поле, участвующее в
hashCode, объект окажется не в своей корзине.containsвернётfalse,removeничего не удалит. Поэтому ключи делают неизменяемыми, с Java 16 для этого удобенrecord; - качество хеша:
return 42;контракт соблюдает, но все ключи падают в одну корзину. С Java 8 корзина превращается в красно-чёрное дерево после восьми элементов (при ёмкости таблицы от 64), но до O(log n) это спасает только ключи сComparable; - сортированные коллекции:
TreeSetиTreeMapсмотрят наcompareTo, а не наequals.new BigDecimal("1.0")иnew BigDecimal("1.00")не равны поequals, но сравниваются как равные: вHashSetбудет два элемента, вTreeSetодин; - наследование:
equalsчерезinstanceofв иерархии с новыми полями ломает симметрию, поэтому выбираютgetClass()или делают классfinal.
Где спотыкаются
Отвечают «всё будет работать» и на этом останавливаются.
Путают направление контракта и говорят, что одинаковый hashCode означает равенство объектов. Это неверно: разные объекты с одним хешем встречаются постоянно, коллизии законны.
Переносят подход на JPA-сущности. equals и hashCode по сгенерированному id меняются после persist, и сущность, положенная в Set до сохранения, в нём теряется. А @Data из Lombok на сущности с двунаправленной связью даёт StackOverflowError в hashCode.