Какой контракт у метода put в мапе при повторном добавлении значения по одному ключу
Что проверяют
Знаете ли вы контракт Map.put из документации, а не только «ну, перезапишется». Следом обычно спрашивают, чем put отличается от putIfAbsent и merge и как мапа решает, что ключ «тот же самый».
Как отвечать
Сигнатура V put(K key, V value). Если ключ уже есть, старое значение заменяется новым, а метод возвращает прежнее значение. Если ключа не было, пара добавляется и возвращается null. Дальше детали, которые отличают сильный ответ:
- ключ не заменяется: в
HashMapостаётся исходный объект ключа, меняется только значение, размер мапы тот же; - неоднозначный
null:nullв ответе значит либо «ключа не было», либо «ключ был связан сnull». Различить помогаетcontainsKey; - что такое «тот же ключ»:
HashMapсравнивает черезhashCodeиequals,TreeMapчерезcompareToилиComparator(результат 0),IdentityHashMapчерез==; - порядок: в
LinkedHashMapповторныйputне сдвигает ключ. Исключение: режимaccessOrder = true, тамputсчитается обращением и переносит запись в конец; - не структурное изменение: замена значения у существующего ключа не считается структурной модификацией, поэтому
ConcurrentModificationExceptionпри итерации не вызывает; - исключения:
UnsupportedOperationExceptionу неизменяемых мап (Map.of,Collections.unmodifiableMap),NullPointerExceptionтам, гдеnullзапрещён, например вConcurrentHashMap.
Если нужна другая семантика, назовите альтернативы: putIfAbsent не трогает существующее значение, а merge и compute объединяют старое и новое, причём в ConcurrentHashMap атомарно.
Где спотыкаются
Говорят, что при повторном ключе put возвращает новое значение или boolean, путая мапу с Set.add.
Используют изменяемый объект как ключ и меняют его поля после вставки. hashCode меняется, повторный put попадает в другую корзину, в мапе появляются два «одинаковых» ключа, а старую запись уже не найти.
Проверяют наличие ключа через map.get(key) != null или по результату put, забывая, что значение само может быть null.