Ответ
В HashMap (и других реализациях Map) равенство ключей определяется строго по контракту методов equals() и hashCode().
Механизм работы:
- При добавлении пары
put(key, value)вычисляется хэш-код ключа (hashCode()). - По хэш-коду находится соответствующий бакет (корзина).
- Внутри бакета ключи сравниваются с помощью метода
equals().
Ключи считаются совпадающими, если:
(key1 == key2) || (key1.hashCode() == key2.hashCode() && key1.equals(key2))
На практике это означает, что equals() возвращает true.
Пример с проблемой:
class BadKey {
String id;
// НЕТ переопределения equals() и hashCode() -> используется Object.equals()
}
Map<BadKey, String> map = new HashMap<>();
BadKey k1 = new BadKey(); k1.id = "a";
BadKey k2 = new BadKey(); k2.id = "a";
map.put(k1, "value1");
map.put(k2, "value2"); // Разные объекты по equals() -> ДВА разных ключа в map!
System.out.println(map.size()); // Вывод: 2
Best Practice: Для кастомных объектов, используемых как ключи, обязательно корректно переопределять equals() и hashCode(), основываясь на одних и тех же значимых полях. Это гарантирует, что логически одинаковые ключи будут корректно находиться и перезаписываться в Map.