Oracle Java ConcurrentHashMap 的错误实现?

Wrong implementation of Oracle Java ConcurrentHashMap?

我正在测试 ConcurrentHashMap Oracle 的 Java 8 实现:

ConcurrentMap<String, String> concurrentMap = new ConcurrentHashMap<>();
String result = concurrentMap.computeIfAbsent("A", k -> "B");
System.out.println(result);  // "B"
result = concurrentMap.putIfAbsent("AA", "BB");
System.out.println(result);  // null

Javadoc of computeIfAbsent确实是这么说的

Implementation Requirements:

The default implementation is equivalent to the following steps for this map, then returning the current value or null if now absent:

if (map.get(key) == null) {
    V newValue = mappingFunction.apply(key);
    if (newValue != null)
        return map.putIfAbsent(key, newValue);
}

它说 然后返回当前值,如果现在不存在则返回 null。那么它不应该返回 null 吗?鉴于 putIfAbsent 也返回 null

我在这里错过了什么?

来自 javadoc 的实际代码:

 if (map.get(key) == null) {
 V newValue = mappingFunction.apply(key);
     if (newValue != null)
         map.put(key, newValue);  // <-
     }
 }

如您所见,标记行中没有 return 关键字。

第 "return" 节还说:

Returns: the current (existing or computed) value associated with the specified key, or null if the computed value is null

ConcurrentMap.computeIfAbsent 的代码示例没有反映实际意图,很可能是 putIfAbsent 的非直觉行为导致的错误,而实现遵循记录的意图。这已在 JDK-8174087 中报告 和 fixed in Java 9

请注意 Map.computeIfAbsent 的合同是

Implementation Requirements:

The default implementation is equivalent to the following steps for this map, then returning the current value or null if now absent:

if (map.get(key) == null) {
    V newValue = mappingFunction.apply(key);
    if (newValue != null)
        map.put(key, newValue);
}

省略了 return 语句。但是明明说

Returns:

the current (existing or computed) value associated with the specified key, or null if the computed value is null

ConcurrentMap.computeIfAbsent 的文档试图合并并发方面,因为 putIfAbsent 的非直观行为而堕落:

Implementation Requirements:

The default implementation is equivalent to the following steps for this map, then returning the current value or null if now absent:

if (map.get(key) == null) {
    V newValue = mappingFunction.apply(key);
    if (newValue != null)
        return map.putIfAbsent(key, newValue);
}

但它仍然显示

Returns:

the current (existing or computed) value associated with the specified key, or null if the computed value is null

并且记录的意图应优先于代码示例。注意实际的 default implementation of 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;
}

所以 the implementation of ConcurrentHashMap.computeIfAbsent 确实符合 ConcurrentMap.computeIfAbsentMap.computeIfAbsent 关于返回值的文档化意图,也等同于 default 提供的实现接口。

为了完整性,Map.computeIfAbsentdefault 实现是

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;
}