Ответ
Использование изменяемых объектов в качестве ключей Map нарушает контракт между hashCode() и equals(), на котором основана работа структур данных, таких как HashMap.
Почему это происходит:
- Хэш-код изменяется: При добавлении объекта-ключа в
Mapвычисляется его хэш-код (hashCode()), который определяет «корзину» (bucket) для хранения пары ключ-значение. - Поиск происходит по новому хэш-коду: Если после добавления изменить состояние ключа так, что его
hashCode()изменится, последующий поиск по этому ключу (map.get(key)) будет выполнен в другой «корзине». В результате элемент не будет найден, хотя физически он присутствует в коллекции. - Нарушается уникальность: Это также может привести к дублированию ключей в разных «корзинах», что противоречит логике
Map.
Пример проблемы:
import java.util.HashMap;
import java.util.Map;
class MutableKey {
private String value;
public MutableKey(String value) { this.value = value; }
public void setValue(String value) { this.value = value; }
@Override
public int hashCode() { return value.hashCode(); }
@Override
public boolean equals(Object obj) { /* ... */ }
}
public class Main {
public static void main(String[] args) {
Map<MutableKey, String> map = new HashMap<>();
MutableKey key = new MutableKey("initial");
map.put(key, "My Value");
// Изменяем состояние ключа, что ведет к изменению hashCode()
key.setValue("modified");
// Попытка найти значение по измененному ключу
System.out.println(map.get(key)); // Вывод: null
// Значение "потерялось", хотя map не пуст
}
}
Решение и best practices:
- Используйте неизменяемые объекты (
String,Integer, собственные классы сfinalполями) в качестве ключей. - Если класс ключа должен быть изменяемым, гарантируйте, что его поля, участвующие в
hashCode()/equals(), не изменяются после того, как объект был использован как ключ. - Корректно переопределяйте методы
hashCode()иequals().