ConcurrentHashMap/ConcurrentMap/Map.compute

Doug Lea dl at cs.oswego.edu
Wed Dec 5 13:10:13 PST 2012


On 12/05/12 07:39, Doug Lea wrote:

> ... method Map.compute(). Three other similar methods
> computeIfAbsent(), computeIfPresent(), and merge()
> amount to special cases that are generally simpler to
> use and more efficient when they apply.
>
> After a few stabs at it, I think that the only sane way
> to spec and default-implement it is to say that, even
> if your Map implementation otherwise allows null keys/values,
> that this method will act as if it doesn't.

Since people seem not to mind this (and Brian's and Tim's
responses on other thread seem to even encourage it), the
set of all four are below. Please skim through and
see if you still agree.

Brian: We had discussed also defining and default-implementing
in Map the other ConcurrentMap methods:

V putIfAbsent(K key, V value);
boolean remove(Object key, Object value);
boolean replace(K key, V oldValue, V newValue);
V replace(K key, V value);

Any thoughts?

....


Additions to interface Map (with actual default implementations
elided for now):


     /**
      * Attempts to compute a mapping given a key and its current
      * mapped value (or {@code null} if there is no current
      * mapping). The default implementation is equivalent to
      *
      *  <pre> {@code
      *   V value = remappingFunction.apply(key, map.get(key));
      *   if (value != null)
      *     map.put(key, value);
      *   else
      *     map.remove(key);
      * }</pre>
      *
      * If the function returns {@code null}, the mapping is removed
      * (or remains absent if initially absent).  If the function
      * itself throws an (unchecked) exception, the exception is
      * rethrown to its caller, and the current mapping is left
      * unchanged.  For example, to either create or append new
      * messages to a value mapping:
      *
      * <pre> {@code
      * Map<Key, String> map = ...;
      * final String msg = ...;
      * map.compute(key, (k, v, msg) => (v == null) ? msg : v + msg)</pre>
      *
      * <p>The default implementation makes no guarantees about
      * synchronization or atomicity properties of this method. Any
      * class overriding this method must specify its concurrency
      * properties.
      *
      * @param key key with which the specified value is to be associated
      * @param remappingFunction the function to compute a value
      * @return the new value associated with the specified key, or null if none
      * @throws NullPointerException if the specified key is null and
      *         this map does not support null keys, or the
      *         remappingFunction is null
      * @throws RuntimeException or Error if the remappingFunction does so,
      *         in which case the mapping is unchanged
      */
     V compute(K key,
               BiFunction<? super K, ? super V, ? extends V> remappingFunction);

     /**
      * If the specified key is not already associated with a value,
      * attempts to compute its value using the given mappingFunction
      * and enters it into the map unless null.  This is equivalent to:
      *
      * <pre> {@code
      * if (map.containsKey(key))
      *   return map.get(key);
      * value = mappingFunction.apply(key);
      * if (value != null)
      *   map.put(key, value);
      * return value;}</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. Any
      * class overriding this method must specify its concurrency
      * properties.
      *
      * @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 RuntimeException or Error if the mappingFunction does so,
      *         in which case the mapping is left unestablished
      */
     V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);

     /**
      * If the given key is present, attempts to compute a new mapping
      * given the key and its current mapped value. This is equivalent
      * to:
      *
      *  <pre> {@code
      *   if (map.containsKey(key)) {
      *     value = remappingFunction.apply(key, map.get(key));
      *     if (value != null)
      *       map.put(key, value);
      *     else
      *       map.remove(key);
      *   }
      * }</pre>
      *
      * If the function returns {@code null}, the mapping is removed
      * (or remains absent if initially absent).  If the function
      * itself throws an (unchecked) exception, the exception is
      * rethrown to its caller, and the current mapping is left
      * unchanged.
      *
      * <p>The default implementation makes no guarantees about
      * synchronization or atomicity properties of this method. Any
      * class overriding this method must specify its concurrency
      * properties.
      *
      * @param key key with which the specified value is to be associated
      * @param remappingFunction the function to compute a value
      * @return the new value associated with the specified key, or null if none
      * @throws NullPointerException if the specified key is null and
      *         this map does not support null keys, or the
      *         remappingFunction is null
      * @throws RuntimeException or Error if the remappingFunction does so,
      *         in which case the mapping is unchanged
      */
     computeIfPresent(K key,
                      BiFunction<? super K, ? super V, ? extends V> 
remappingFunction);

     /**
      * If the specified key is not already associated with a value,
      * associates it with the given value.  Otherwise, replaces the
      * value with the results of the given remapping function, or
      * removes if {@code null}. This is equivalent to:
      *
      *  <pre> {@code
      *   V newValue;
      *   if (!map.containsKey(key))
      *     newValue = value;
      *   else
      *     newValue = remappingFunction.apply(map.get(key), value);
      *   if (newValue != null)
      *     map.put(key, newValue);
      *   else
      *     map.remove(key);
      * }</pre>
      *
      * If the function returns {@code null}, the mapping is removed
      * (or remains absent if initially absent).  If the function
      * itself throws an (unchecked) exception, the exception is
      * rethrown to its caller, and the current mapping is left
      * unchanged.
      *
      * <p>The default implementation makes no guarantees about
      * synchronization or atomicity properties of this method. Any
      * class overriding this method must specify its concurrency
      * properties.
      *
      * @param key key with which the specified value is to be associated
      * @param value the value to use if absent
      * @param remappingFunction the function to recompute a value if present
      * @return the new value associated with the specified key, or null if none
      * @throws NullPointerException if the specified key is null and
      *         this map does not support null keys, or the
      *         remappingFunction is null
      * @throws RuntimeException or Error if the remappingFunction does so,
      *         in which case the mapping is unchanged
      */
     V merge(K key, V value,
             BiFunction<? super V, ? super V, ? extends V> remappingFunction);





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