Ответ
Потеря значения в HashMap происходит, если изменяется состояние ключа, влияющее на его hashCode() или equals(), после того как он был помещен в мапу. Хотя ключ объявлен как final, его изменяемость может быть нарушена.
Механизм потери:
- Объект-ключ помещается в
HashMap. Его хеш-код вычисляется и используется для определения корзины (bucket). - Состояние ключа изменяется (например, через рефлексию), и его
hashCode()возвращает новое значение. - При попытке найти значение по тому же объекту-ключу
HashMapищет его в корзине, соответствующей новому хеш-коду. Поскольку объект лежит в корзине по старому хеш-коду, поиск возвращаетnull.
Пример нарушения иммутабельности через рефлексию:
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public final class MutableKey {
private final int id; // Поле final
public MutableKey(int id) { this.id = id; }
@Override public int hashCode() { return id; }
// Геттер, equals...
}
public class HashMapLossExample {
public static void main(String[] args) throws Exception {
MutableKey key = new MutableKey(1);
Map<MutableKey, String> map = new HashMap<>();
map.put(key, "Important Value");
System.out.println("До изменения: " + map.get(key)); // "Important Value"
// Нарушаем иммутабельность через рефлексию
Field idField = MutableKey.class.getDeclaredField("id");
idField.setAccessible(true);
idField.setInt(key, 2); // Меняем значение final-поля!
System.out.println("После изменения: " + map.get(key)); // null - ПОТЕРЯ!
}
}
Решение и Best Practices:
- Создавайте ключи полностью иммутабельными: все поля
final, нет сеттеров, класс может бытьfinal. - Используйте для ключей простые типы (
String,Integer) или создавайте собственные неизменяемые классы. - Никогда не изменяйте объект, который используется как ключ в
HashMapилиHashSet.