Где искать ошибку, если при получении значения из HashMap по ключу возвращается null, хотя ключ не менялся
Что проверяют
Понимаете ли вы, как HashMap ищет ключ: сначала корзина по hashCode(), потом сравнение через equals(). Фраза «ключ не менялся» здесь намеренная, интервьюер ждёт, что вы в ней усомнитесь. Следом часто просят написать правильный ключ на Kotlin или объяснить, чем заменить HashMap при работе из нескольких потоков.
Как отвечать
Идите по причинам от самой частой:
- нет
equalsиhashCode: новый экземпляр с теми же полями считается другим объектом. В Kotlin спасаетdata class, но он учитывает только свойства из основного конструктора; - ключ мутировал после
put: ссылка та же, но поле, от которого зависитhashCode, изменили в другом месте. Запись осталась в старой корзине, и её не найти даже тем же объектом. Поэтому ключи делают неизменяемыми, в Kotlin черезval; - массив в ключе:
ByteArrayиIntArrayсравниваются по ссылке, в том числе внутриdata class; - другой тип ключа:
getпринимаетObject, поэтому поискIntegerвMap<Long, V>из Java компилируется и возвращаетnull. То же сStringBuilderвместоString; - в карте лежит
null:put(key, null)допустим, отличить его от отсутствия ключа помогаетcontainsKey; - многопоточность:
HashMapне потокобезопасен, запись из фонового потока и чтение с главного теряют данные. Решения:ConcurrentHashMap(он не принимаетnull), работа из одного диспетчера корутин илиMutex; - особенности Android: после пересоздания
Activityили смерти процесса вы читаете уже другой экземпляр карты, записи вытесняетLruCache, а изWeakHashMapих убирает сборщик мусора.
Для диагностики: посмотреть map.keys в отладчике, сравнить hashCode ключа при put и при get, поставить условный брейкпоинт на put.
Где спотыкаются
Добавляют проверку containsKey и считают вопрос закрытым. Она зависит от тех же hashCode и equals, поэтому причину не объясняет.
Переопределяют equals без hashCode или считают hashCode по изменяемым полям.
Оборачивают всё в Collections.synchronizedMap и не могут объяснить, в чём была гонка.