ConcurrentHashMap/ConcurrentMap/Map.compute

Brian Goetz brian.goetz at oracle.com
Fri Dec 14 08:46:18 PST 2012


No complaint from me.  The alternative seems worse; it says that because 
of the existence of null-supporting maps, no one can have these nice new 
features.

But, I am not quite following your argument.  Why does it fail if you 
replace "map.get(key) == null" with "!map.containsKey(key)"?  For 
non-null-accepting maps they are equivalent.

On 12/14/2012 9:52 AM, Doug Lea wrote:
>
> Back to this after several diversions...
>
> On 12/07/12 11:16, Doug Lea wrote:
>>> Basic idea: defaults for function-accepting Map methods are solely
>>> in terms of the 4 CM methods, which are in turn non-atomic for
>>> non-CM....
>>
>
> Unfortunately the null-value ambiguity hits yet again when
> moving from writing specs to writing default implementations.
> (Have I mentioned lately how terrible it is to allow nulls? :-)
>
> The defaults for function-accepting methods must rely not
> only on these 4 CHM methods, but also on get and/or containsKey.
> For null-accepting maps, you need the pair of them
> to default-implement (non-atomically) but for others,
> you must not use the pair of them (just get) to propagate
> the property that if putIfAbsent is thread-safe then so is
> computeIfAbsent.
>
> The only way out I see  is to even further sacrifice
> sensibility for null-accepting maps, by saying that the
> methods are allowed to treat absence and mapping to null
> identically and that the default implementation does so.
> Here's computeIfAbsent. Any complaints?
>
>      /**
>       * If the specified key is not already associated with a value (or
>       * is mapped to {@code null)), attempts to compute its value using
>       * the given mapping function and enters it into the map unless
>       * {@code null}.  The default implementation is equivalent to the
>       * following, then returning the current value or {@code null} if
>       * absent:
>       *
>       * <pre> {@code
>       * if (map.get(key) == null) {
>       *   V newValue = mappingFunction.apply(key);
>       *   if (newValue != null)
>       *      map.putIfAbsent(key, newValue);
>       * }}</pre>
>       *
>       * If the function returns {@code null} no mapping is recorded. If
>       * the function itself throws an (unchecked) exception, the
>       * exception is rethrown to its caller, and no mapping is
>       * recorded.  The most common usage is to construct a new object
>       * serving as an initial mapped value or memoized result, as in:
>       *
>       *  <pre> {@code
>       * map.computeIfAbsent(key, k -> new Value(f(k)));} </pre>
>       *
>       * <p>The default implementation makes no guarantees about
>       * synchronization or atomicity properties of this method or the
>       * application of the mapping function. Any class overriding this
>       * method must specify its concurrency properties.  In particular,
>       * all implementations of subinterface {@link
>       * java.util.concurrent.ConcurrentMap} must document whether the
>       * function is applied once atomically only if the value is not
>       * present.  Any class that permits null values must document
>       * whether and how this method distinguishes absence from null
>       * mappings.
>       *
>       * @param key key with which the specified value is to be associated
>       * @param mappingFunction the function to compute a value
>       * @return the current (existing or computed) value associated with
>       *         the specified key, or null if the computed value is null
>       * @throws NullPointerException if the specified key is null and
>       *         this map does not support null keys, or the
>       *         mappingFunction is null
>       * @throws UnsupportedOperationException if the <tt>put</tt> operation
>       *         is not supported by this map
>       * @throws ClassCastException if the class of the specified key or
> value
>       *         prevents it from being stored in this map
>       * @throws RuntimeException or Error if the mappingFunction does so,
>       *         in which case the mapping is left unestablished
>       */
>       default V computeIfAbsent(K key, Function<? super K, ? extends V>
> mappingFunction) {
>           V v, newValue;
>           return ((v = get(key)) == null &&
>                   (newValue = mappingFunction.apply(key)) != null &&
>                   (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
>       }
>


More information about the lambda-libs-spec-observers mailing list