jdk/src/share/classes/java/util/Map.java
changeset 21352 0372edc9a995
parent 21339 20e8b81964d5
child 22055 d9836bf9992a
--- a/jdk/src/share/classes/java/util/Map.java	Wed Oct 23 20:51:14 2013 +0100
+++ b/jdk/src/share/classes/java/util/Map.java	Wed Oct 23 14:32:41 2013 -0700
@@ -565,7 +565,8 @@
      * Returns the value to which the specified key is mapped, or
      * {@code defaultValue} if this map contains no mapping for the key.
      *
-     * <p>The default implementation makes no guarantees about synchronization
+     * @implSpec
+     * The default implementation makes no guarantees about synchronization
      * or atomicity properties of this method. Any implementation providing
      * atomicity guarantees must override this method and document its
      * concurrency properties.
@@ -596,22 +597,18 @@
      * the order of entry set iteration (if an iteration order is specified.)
      * Exceptions thrown by the action are relayed to the caller.
      *
-     * <p>The default implementation should be overridden by implementations if
-     * they can provide a more performant implementation than an iterator-based
-     * one.
-     *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-     *
-     * @implSpec The default implementation is equivalent to, for this
-     * {@code map}:
+     * @implSpec
+     * The default implementation is equivalent to, for this {@code map}:
      * <pre> {@code
      * for ((Map.Entry<K, V> entry : map.entrySet())
      *     action.accept(entry.getKey(), entry.getValue());
      * }</pre>
      *
+     * The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+     *
      * @param action The action to be performed for each entry
      * @throws NullPointerException if the specified action is null
      * @throws ConcurrentModificationException if an entry is found to be
@@ -640,11 +637,6 @@
      * function throws an exception.  Exceptions thrown by the function are
      * relayed to the caller.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-     *
      * @implSpec
      * <p>The default implementation is equivalent to, for this {@code map}:
      * <pre> {@code
@@ -652,6 +644,11 @@
      *     entry.setValue(function.apply(entry.getKey(), entry.getValue()));
      * }</pre>
      *
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+     *
      * @param function the function to apply to each entry
      * @throws UnsupportedOperationException if the {@code set} operation
      * is not supported by this map's entry set iterator.
@@ -703,22 +700,23 @@
      * to {@code null}) associates it with the given value and returns
      * {@code null}, else returns the current value.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-     *
      * @implSpec
      * The default implementation is equivalent to, for this {@code
      * map}:
      *
      * <pre> {@code
-     * if (map.get(key) == null)
-     *     return map.put(key, value);
-     * else
-     *     return map.get(key);
+     * V v = map.get(key);
+     * if (v == null)
+     *     v = map.put(key, value);
+     *
+     * return v;
      * }</pre>
      *
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+     *
      * @param key key with which the specified value is to be associated
      * @param value value to be associated with the specified key
      * @return the previous value associated with the specified key, or
@@ -738,16 +736,12 @@
      * @throws IllegalArgumentException if some property of the specified key
      *         or value prevents it from being stored in this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
-     * @throws ConcurrentModificationException if a modification of the map is
-     * detected during insertion of the value.
      * @since 1.8
      */
     default V putIfAbsent(K key, V value) {
         V v = get(key);
         if (v == null) {
-            if (put(key, value) != null) {
-                throw new ConcurrentModificationException();
-            }
+            v = put(key, value);
         }
 
         return v;
@@ -757,11 +751,6 @@
      * Removes the entry for the specified key only if it is currently
      * mapped to the specified value.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-     *
      * @implSpec
      * The default implementation is equivalent to, for this {@code map}:
      *
@@ -773,6 +762,11 @@
      *     return false;
      * }</pre>
      *
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+     *
      * @param key key with which the specified value is associated
      * @param value value expected to be associated with the specified key
      * @return {@code true} if the value was removed
@@ -801,11 +795,6 @@
      * Replaces the entry for the specified key only if currently
      * mapped to the specified value.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-    *
      * @implSpec
      * The default implementation is equivalent to, for this {@code map}:
      *
@@ -821,6 +810,11 @@
      * for maps that do not support null values if oldValue is null unless
      * newValue is also null.
      *
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+     *
      * @param key key with which the specified value is associated
      * @param oldValue value expected to be associated with the specified key
      * @param newValue value to be associated with the specified key
@@ -853,11 +847,6 @@
      * Replaces the entry for the specified key only if it is
      * currently mapped to some value.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document its
-     * concurrency properties.
-     *
      * @implSpec
      * The default implementation is equivalent to, for this {@code map}:
      *
@@ -868,6 +857,11 @@
      *     return null;
      * }</pre>
      *
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document its
+     * concurrency properties.
+      *
      * @param key key with which the specified value is associated
      * @param value value to be associated with the specified key
      * @return the previous value associated with the specified key, or
@@ -888,14 +882,17 @@
      * @since 1.8
      */
     default V replace(K key, V value) {
-        return containsKey(key) ? put(key, value) : null;
+        V curValue;
+        if (((curValue = get(key)) != null) || containsKey(key)) {
+            curValue = put(key, value);
+        }
+        return curValue;
     }
 
     /**
-     * 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 this map unless
-     * {@code null}.
+     * 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 this map unless {@code null}.
      *
      * <p>If the function returns {@code null} no mapping is recorded. If
      * the function itself throws an (unchecked) exception, the
@@ -907,35 +904,42 @@
      * map.computeIfAbsent(key, k -> new Value(f(k)));
      * }</pre>
      *
+     * <p>Or to implement a multi-value map, {@code Map<K,Collection<V>>},
+     * supporting multiple values per key:
+     *
+     * <pre> {@code
+     * map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
+     * }</pre>
+     *
+     *
+     * @implSpec
+     * The default implementation is equivalent to the following steps for this
+     * {@code map}, then returning the current value or {@code null} if now
+     * absent:
+     *
+     * <pre> {@code
+     * if (map.get(key) == null) {
+     *     V newValue = mappingFunction.apply(key);
+     *     if (newValue != null)
+     *         map.put(key, newValue);
+     * }
+     * }</pre>
+     *
      * <p>The default implementation makes no guarantees about synchronization
      * or atomicity properties of this method. Any implementation providing
      * atomicity guarantees must override this method and document 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.
-     *
-     * @implSpec
-     * The default implementation is equivalent to the following
-     * steps for this {@code map}, then returning the current value or
-     * {@code null} if now absent:
-     *
-     * <pre> {@code
-     * if (map.get(key) == null) {
-     *     V newValue = mappingFunction.apply(key);
-     *     if (newValue != null)
-     *         map.putIfAbsent(key, newValue);
-     * }
-     * }</pre>
+     * present.
      *
      * @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
+     *         this map does not support null keys, or the mappingFunction
+     *         is null
      * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
@@ -947,10 +951,16 @@
     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;
+        V v;
+        if ((v = get(key)) == null) {
+            V newValue;
+            if ((newValue = mappingFunction.apply(key)) != null) {
+                put(key, newValue);
+                return newValue;
+            }
+        }
+
+        return v;
     }
 
     /**
@@ -960,6 +970,22 @@
      * <p>If the function returns {@code null}, the mapping is removed.  If the
      * function itself throws an (unchecked) exception, the exception is
      * rethrown, and the current mapping is left unchanged.
+    *
+     * @implSpec
+     * The default implementation is equivalent to performing the following
+     * steps for this {@code map}, then returning the current value or
+     * {@code null} if now absent:
+     *
+     * <pre> {@code
+     * if (map.get(key) != null) {
+     *     V oldValue = map.get(key);
+     *     V newValue = remappingFunction.apply(key, oldValue);
+     *     if (newValue != null)
+     *         map.put(key, newValue);
+     *     else
+     *         map.remove(key);
+     * }
+     * }</pre>
      *
      * <p>The default implementation makes no guarantees about synchronization
      * or atomicity properties of this method. Any implementation providing
@@ -967,27 +993,7 @@
      * 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.
-     *
-     * @implSpec
-     * The default implementation is equivalent to performing the
-     * following steps for this {@code map}, then returning the
-     * current value or {@code null} if now absent:
-     *
-     * <pre> {@code
-     * if (map.get(key) != null) {
-     *     V oldValue = map.get(key);
-     *     V newValue = remappingFunction.apply(key, oldValue);
-     *     if (newValue != null)
-     *         map.replace(key, oldValue, newValue);
-     *     else
-     *         map.remove(key, oldValue);
-     * }
-     * }</pre>
-     *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
+     * present.
      *
      * @param key key with which the specified value is to be associated
      * @param remappingFunction the function to compute a value
@@ -1007,22 +1013,25 @@
             BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
         Objects.requireNonNull(remappingFunction);
         V oldValue;
-        while ((oldValue = get(key)) != null) {
+        if ((oldValue = get(key)) != null) {
             V newValue = remappingFunction.apply(key, oldValue);
             if (newValue != null) {
-                if (replace(key, oldValue, newValue))
-                    return newValue;
-            } else if (remove(key, oldValue))
+                put(key, newValue);
+                return newValue;
+            } else {
+                remove(key);
                 return null;
+            }
+        } else {
+            return null;
         }
-        return oldValue;
     }
 
     /**
-     * Attempts to compute a mapping for the specified key and its
-     * current mapped value (or {@code null} if there is no current
-     * mapping). For example, to either create or append a {@code
-     * String msg} to a value mapping:
+     * Attempts to compute a mapping for the specified key and its current
+     * mapped value (or {@code null} if there is no current mapping). For
+     * example, to either create or append a {@code String} msg to a value
+     * mapping:
      *
      * <pre> {@code
      * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
@@ -1033,15 +1042,6 @@
      * (unchecked) exception, the exception is rethrown, and the current mapping
      * is left unchanged.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document 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.
-     *
      * @implSpec
      * The default implementation is equivalent to performing the following
      * steps for this {@code map}, then returning the current value or
@@ -1052,19 +1052,24 @@
      * V newValue = remappingFunction.apply(key, oldValue);
      * if (oldValue != null ) {
      *    if (newValue != null)
-     *       map.replace(key, oldValue, newValue);
+     *       map.put(key, newValue);
      *    else
-     *       map.remove(key, oldValue);
+     *       map.remove(key);
      * } else {
      *    if (newValue != null)
-     *       map.putIfAbsent(key, newValue);
+     *       map.put(key, newValue);
      *    else
      *       return null;
      * }
      * }</pre>
      *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document 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.
      *
      * @param key key with which the specified value is to be associated
      * @param remappingFunction the function to compute a value
@@ -1084,44 +1089,22 @@
             BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
         Objects.requireNonNull(remappingFunction);
         V oldValue = get(key);
-        for (;;) {
-            V newValue = remappingFunction.apply(key, oldValue);
-            if (newValue == null) {
-                // delete mapping
-                if(oldValue != null || containsKey(key)) {
-                    // something to remove
-                    if (remove(key, oldValue)) {
-                        // removed the old value as expected
-                        return null;
-                    }
 
-                    // some other value replaced old value. try again.
-                    oldValue = get(key);
-                } else {
-                    // nothing to do. Leave things as they were.
-                    return null;
-                }
+        V newValue = remappingFunction.apply(key, oldValue);
+        if (newValue == null) {
+            // delete mapping
+            if (oldValue != null || containsKey(key)) {
+                // something to remove
+                remove(key);
+                return null;
             } else {
-                // add or replace old mapping
-                if (oldValue != null) {
-                    // replace
-                    if (replace(key, oldValue, newValue)) {
-                        // replaced as expected.
-                        return newValue;
-                    }
-
-                    // some other value replaced old value. try again.
-                    oldValue = get(key);
-                } else {
-                    // add (replace if oldValue was null)
-                    if ((oldValue = putIfAbsent(key, newValue)) == null) {
-                        // replaced
-                        return newValue;
-                    }
-
-                    // some other value replaced old value. try again.
-                }
+                // nothing to do. Leave things as they were.
+                return null;
             }
+        } else {
+            // add or replace old mapping
+            put(key, newValue);
+            return newValue;
         }
     }
 
@@ -1143,15 +1126,6 @@
      * (unchecked) exception, the exception is rethrown, and the current mapping
      * is left unchanged.
      *
-     * <p>The default implementation makes no guarantees about synchronization
-     * or atomicity properties of this method. Any implementation providing
-     * atomicity guarantees must override this method and document 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.
-     *
      * @implSpec
      * The default implementation is equivalent to performing the
      * following steps for this {@code map}, then returning the
@@ -1162,15 +1136,20 @@
      * V newValue = (oldValue == null) ? value :
      *              remappingFunction.apply(oldValue, value);
      * if (newValue == null)
-     *     map.remove(key, oldValue);
+     *     map.remove(key);
      * else if (oldValue == null)
-     *     map.putIfAbsent(key, newValue);
+     *     map.remove(key);
      * else
-     *     map.replace(key, oldValue, newValue);
+     *     map.put(key, newValue);
      * }</pre>
      *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
+     * <p>The default implementation makes no guarantees about synchronization
+     * or atomicity properties of this method. Any implementation providing
+     * atomicity guarantees must override this method and document 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.
      *
      * @param key key with which the specified value is to be associated
      * @param value the value to use if absent
@@ -1183,32 +1162,30 @@
      *         prevents it from being stored in this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws NullPointerException if the specified key is null and
-     *         this map does not support null keys, or the
-     *         remappingFunction is null
+     *         this map does not support null keys, or the remappingFunction
+     *         is null
      * @since 1.8
      */
     default V merge(K key, V value,
             BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
         Objects.requireNonNull(remappingFunction);
         V oldValue = get(key);
-        for (;;) {
-            if (oldValue != null) {
-                V newValue = remappingFunction.apply(oldValue, value);
-                if (newValue != null) {
-                    if (replace(key, oldValue, newValue))
-                        return newValue;
-                } else if (remove(key, oldValue)) {
-                    return null;
-                }
-                oldValue = get(key);
+        if (oldValue != null) {
+            V newValue = remappingFunction.apply(oldValue, value);
+            if (newValue != null) {
+                put(key, newValue);
+                return newValue;
             } else {
-                if (value == null) {
-                    return null;
-                }
-
-                if ((oldValue = putIfAbsent(key, value)) == null) {
-                    return value;
-                }
+                remove(key);
+                return null;
+            }
+        } else {
+            if (value == null) {
+                remove(key);
+                return null;
+            } else {
+                put(key, value);
+                return value;
             }
         }
     }