Ответ 1
Пример кода ConcurrentMap.computeIfAbsent
не отражает фактическое намерение, скорее всего, ошибку, вызванную неинтуитивным поведением putIfAbsent
, в то время как реализация подчиняется документированному намерению. Об этом сообщается в JDK-8174087
и исправлено в Java 9
Обратите внимание, что контракт Map.computeIfAbsent
заключен в
Требования к реализации:
Реализация по умолчанию эквивалентна следующим шагам для этой карты, а затем возвращает текущее значение или значение null, если оно отсутствует:
if (map.get(key) == null) { V newValue = mappingFunction.apply(key); if (newValue != null) map.put(key, newValue); }
без оператора return
. Но ясно сказано
Возврат:
текущее (существующее или вычисленное) значение, связанное с указанным ключом, или значение null, если вычисленное значение равно null
Это документация ConcurrentMap.computeIfAbsent
, которая пытается включить аспект concurrency, падая за неинтуитивное поведение putIfAbsent
:
Требования к реализации:
Реализация по умолчанию эквивалентна следующим шагам для этой карты, а затем возвращает текущее значение или значение null, если оно отсутствует:
if (map.get(key) == null) { V newValue = mappingFunction.apply(key); if (newValue != null) return map.putIfAbsent(key, newValue); }
но он все еще говорит
Возврат:
текущее (существующее или вычисленное) значение, связанное с указанным ключом, или значение null, если вычисленное значение равно null
и задокументированное намерение должно иметь приоритет над примером кода. Обратите внимание, что фактическая реализация default
ConcurrentMap.computeIfAbsent
соответствует документированному намерению:
@Override default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { Objects.requireNonNull(mappingFunction); V v, newValue; return ((v = get(key)) == null && (newValue = mappingFunction.apply(key)) != null && (v = putIfAbsent(key, newValue)) == null) ? newValue : v; }
Итак реализация ConcurrentHashMap.computeIfAbsent
соответствует документированному намерению обоих, ConcurrentMap.computeIfAbsent
и Map.computeIfAbsent
относительно возвращаемого значения и также эквивалентна реализации default
, предоставляемой интерфейсами.
Для полноты реализации default
Map.computeIfAbsent
является
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { Objects.requireNonNull(mappingFunction); V v; if ((v = get(key)) == null) { V newValue; if ((newValue = mappingFunction.apply(key)) != null) { put(key, newValue); return newValue; } } return v; }