src/share/classes/java/util/Map.java

Print this page
rev 8193 : [mq]: JDK-8024688.2

@@ -575,10 +575,11 @@
     * this map
     * (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if the specified key is null and this map
     * does not permit null keys
     * (<a href="Collection.html#optional-restrictions">optional</a>)
+    * @since 1.8
     */
     default V getOrDefault(Object key, V defaultValue) {
         V v;
         return (((v = get(key)) != null) || containsKey(key))
             ? v

@@ -786,11 +787,13 @@
         Object curValue = get(key);
         if (!Objects.equals(curValue, value) ||
             (curValue == null && !containsKey(key))) {
             return false;
         }
-        remove(key);
+        if(curValue != remove(key)) {
+            throw new ConcurrentModificationException("value unexpectedly changed");
+        }
         return true;
     }
 
     /**
      * Replaces the entry for the specified key only if currently

@@ -838,13 +841,15 @@
         Object curValue = get(key);
         if (!Objects.equals(curValue, oldValue) ||
             (curValue == null && !containsKey(key))) {
             return false;
         }
-        put(key, newValue);
+        if(curValue == put(key, newValue)) {
         return true;
     }
+        throw new ConcurrentModificationException("value unexpectedly changed");
+    }
 
     /**
      * Replaces the entry for the specified key only if it is
      * currently mapped to some value.
      *

@@ -881,11 +886,19 @@
      * @throws IllegalArgumentException if some property of the specified key
      *         or value prevents it from being stored in this map
      * @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)) {
+            if(curValue == put(key, value)) {
+                return curValue;
+            }
+        } else {
+            return curValue;
+        }
+        throw new ConcurrentModificationException("value unexpectedly changed");
     }
 
     /**
      * If the specified key is not already associated with a value (or
      * is mapped to {@code null}), attempts to compute its value using

@@ -906,12 +919,11 @@
      * 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.
+     * present.
      *
      * @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:

@@ -940,14 +952,21 @@
      * @since 1.8
      */
     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) {
+                if (null == putIfAbsent(key, newValue))
+                    return newValue;
+                throw new ConcurrentModificationException("expected value changed");
+            }
+        }
+
+        return v;
     }
 
     /**
      * If the value for the specified key is present and non-null, attempts to
      * compute a new mapping given the key and its current mapped value.

@@ -979,13 +998,10 @@
      *     else
      *         map.remove(key, oldValue);
      * }
      * }</pre>
      *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
-     *
      * @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

@@ -1000,19 +1016,21 @@
      */
     default V computeIfPresent(K key,
             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))
                 return null;
+        } else {
+            return null;
         }
-        return oldValue;
+        throw new ConcurrentModificationException("unexpected value change");
     }
 
     /**
      * Attempts to compute a mapping for the specified key and its
      * current mapped value (or {@code null} if there is no current

@@ -1056,13 +1074,10 @@
      *    else
      *       return null;
      * }
      * }</pre>
      *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
-     *
      * @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

@@ -1077,23 +1092,20 @@
      */
     default V compute(K key,
             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;
                 }
             } else {

@@ -1102,24 +1114,19 @@
                     // 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) {
+                if (putIfAbsent(key, newValue) == null) {
                         // replaced
                         return newValue;
                     }
-
-                    // some other value replaced old value. try again.
-                }
             }
         }
+        throw new ConcurrentModificationException("expected value change");
     }
 
     /**
      * If the specified key is not already associated with a value or is
      * associated with null, associates it with the given value.

@@ -1142,12 +1149,11 @@
      * 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.
+     * present.
      *
      * @implSpec
      * The default implementation is equivalent to performing the
      * following steps for this {@code map}, then returning the
      * current value or {@code null} if absent:

@@ -1162,13 +1168,10 @@
      *     map.putIfAbsent(key, newValue);
      * else
      *     map.replace(key, oldValue, newValue);
      * }</pre>
      *
-     * In concurrent contexts, the default implementation may retry
-     * these steps when multiple threads attempt updates.
-     *
      * @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 UnsupportedOperationException if the {@code put} operation

@@ -1184,27 +1187,27 @@
      */
     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);
             } else {
                 if (value == null) {
+                if(remove(key) == null) {
                     return null;
                 }
-
-                if ((oldValue = putIfAbsent(key, value)) == null) {
+            } else {
+                if (putIfAbsent(key, value) == null) {
                     return value;
                 }
             }
         }
+        throw new ConcurrentModificationException("unexpected value change");
     }
 }