Что произойдет, если использовать изменяемый (mutable) объект в качестве ключа в Map?

«Что произойдет, если использовать изменяемый (mutable) объект в качестве ключа в Map?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключ может стать "потерянным" в Map после изменения, что приведет к непредсказуемому поведению.

Проблема: HashMap использует хэш-код ключа для определения бакета. Если ключ изменяется после добавления, его хэш-код может измениться, но запись останется в старом бакете.

Пример:

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 o) { /* реализация */ }
}

Map<MutableKey, String> map = new HashMap<>();
MutableKey key = new MutableKey("initial");
map.put(key, "value");

// Изменяем ключ - НАРУШАЕМ КОНТРАКТ!
key.setValue("modified");

// Проблемы:
System.out.println(map.get(key)); // null - запись не найдена
System.out.println(map.containsKey(key)); // false

// Но запись все еще существует в map!
map.forEach((k, v) -> System.out.println(v)); // Выведет "value"

Последствия:

  1. Потеря доступа к значению через измененный ключ
  2. Утечка памяти - запись остается в map, но недоступна
  3. Нарушение контракта Map (ключи должны быть immutable)

Решение:

  • Используйте неизменяемые ключи (String, Integer, LocalDate)
  • Если ключ должен меняться - удалите старую запись и добавьте новую
  • Для кастомных ключей:
    • Сделайте поля final
    • Переопределите equals() и hashCode() на основе final-полей
    • Реализуйте Comparable для TreeMap