HashMap ищет значение не просто по содержимому объекта. Сначала он использует hashCode ключа, чтобы выбрать внутреннюю корзину, а потом уже сравнивает ключи через equals.
Представим ключ, где hashCode зависит от поля name:
class UserKey {
String name;
UserKey(String name) {
this.name = name;
}
}Если такой объект положить в map, всё выглядит нормально:
var key = new UserKey("alice");
map.put(key, "admin");Проблема начинается, если после вставки изменить поле, участвующее в equals или hashCode.
Например так:
key.name = "bob";
System.out.println(map.get(key));
Теперь объект физически лежит в той корзине, которая была рассчитана для alice. Но при поиске HashMap уже считает хеш для bob и идёт искать в другое место.
Из-за этого даже containsKey может вернуть false:
System.out.println(map.containsKey(key));
Это выглядит очень странно, потому что ссылка на объект осталась той же. Но для HashMap важна стабильность equals и hashCode на всё время, пока объект используется как ключ.
Надёжнее делать ключ неизменяемым:
record UserKey(String name) {}Если нужен обычный класс, поля ключа лучше сделать final и не давать сеттеры. Тогда объект не сможет поменять свой hashCode после добавления в map.
Полезные ресурсы по Java


