changeset 8089:7d9995af08e1

sync Map defaults with TL.
author mduigou
date Thu, 11 Apr 2013 12:54:30 -0700
parents 5e7f8797b9c1
children e8eea7e26b32
files src/share/classes/java/util/Collections.java src/share/classes/java/util/Hashtable.java src/share/classes/java/util/Map.java src/share/classes/java/util/concurrent/ConcurrentMap.java test/java/util/Map/Defaults.java
diffstat 5 files changed, 270 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/Collections.java	Thu Apr 11 15:53:17 2013 -0400
+++ b/src/share/classes/java/util/Collections.java	Thu Apr 11 12:54:30 2013 -0700
@@ -2249,52 +2249,42 @@
         public V getOrDefault(Object k, V defaultValue) {
             synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
         }
-
         @Override
         public void forEach(BiConsumer<? super K, ? super V> action) {
             synchronized (mutex) {m.forEach(action);}
         }
-
         @Override
         public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
             synchronized (mutex) {m.replaceAll(function);}
         }
-
         @Override
         public V putIfAbsent(K key, V value) {
             synchronized (mutex) {return m.putIfAbsent(key, value);}
         }
-
         @Override
         public boolean remove(Object key, Object value) {
             synchronized (mutex) {return m.remove(key, value);}
         }
-
         @Override
         public boolean replace(K key, V oldValue, V newValue) {
             synchronized (mutex) {return m.replace(key, oldValue, newValue);}
         }
-
         @Override
         public V replace(K key, V value) {
             synchronized (mutex) {return m.replace(key, value);}
         }
-
         @Override
         public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
             synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
         }
-
         @Override
         public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
             synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
         }
-
         @Override
         public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
             synchronized (mutex) {return m.compute(key, remappingFunction);}
         }
-
         @Override
         public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
             synchronized (mutex) {return m.merge(key, value, remappingFunction);}
--- a/src/share/classes/java/util/Hashtable.java	Thu Apr 11 15:53:17 2013 -0400
+++ b/src/share/classes/java/util/Hashtable.java	Thu Apr 11 12:54:30 2013 -0700
@@ -498,7 +498,7 @@
     public synchronized V put(K key, V value) {
         // Make sure the value is not null
         if (value == null) {
-            throw new NullPointerException("null value for " + key);
+            throw new NullPointerException();
         }
 
         // Makes sure the key is not already in the hashtable.
--- a/src/share/classes/java/util/Map.java	Thu Apr 11 15:53:17 2013 -0400
+++ b/src/share/classes/java/util/Map.java	Thu Apr 11 12:54:30 2013 -0700
@@ -486,6 +486,11 @@
     *  or {@code defaultValue} if this map contains no mapping
     *  for the key.
     *
+    * <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 the key whose associated value is to be returned
     * @return the value to which the specified key is mapped, or
     * {@code defaultValue} if this map contains no mapping for the key
@@ -498,9 +503,9 @@
     */
     default V getOrDefault(Object key, V defaultValue) {
         V v;
-        return (null != (v = get(key)))
+        return (((v = get(key)) != null) || containsKey(key))
             ? v
-            : containsKey(key) ? null : defaultValue;
+            : defaultValue;
     }
 
     /**
@@ -513,15 +518,13 @@
      * 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
-     * class overriding this method must specify its concurrency
-     * properties. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
+     * <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}:
+     * @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());
@@ -529,29 +532,35 @@
      *
      * @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
+     * removed during iteration
      * @since 1.8
      */
     default void forEach(BiConsumer<? super K, ? super V> action) {
         Objects.requireNonNull(action);
         for (Map.Entry<K, V> entry : entrySet()) {
-            action.accept(entry.getKey(), entry.getValue());
+            K k;
+            V v;
+            try {
+               k = entry.getKey();
+               v = entry.getValue();
+            } catch(IllegalStateException ise) {
+                throw new ConcurrentModificationException(ise);
+            }
+            action.accept(k, v);
         }
     }
 
     /**
-     * Apply the specified function to each entry in this map, in the
-     * order entries are returned by an entry set iterator, and replacing
-     * each entry's value with the result of calling the function's
-     * {@link BiFunction#apply(Object, Object) BiFunction.apply(K key, V, value)}
-     * method with the current entry's key and value.  Exceptions thrown by the
-     * function are relayed to the caller.
+     * Replaces each entry's value with the result of invoking the given
+     * function on that entry, in the order entries are returned by an entry
+     * set iterator, until all entries have been processed or the function
+     * throws an exception.
      *
-     * <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. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
+     * <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}:
@@ -561,24 +570,38 @@
      * }</pre>
      *
      * @param function the function to apply to each entry
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
-     *         is not supported by this map
-     * @throws ClassCastException if the class of the specified value
-     *         prevents it from being stored in the backing map
+     * @throws UnsupportedOperationException if the {@code set} operation
+     * is not supported by this map's entry set iterator.
+     * @throws ClassCastException if the class of a replacement value
+     * prevents it from being stored in this map
      * @throws NullPointerException if the specified function is null, or the
-     *         specified key or value is null, and this map does not permit null
-     *         keys or values
-     * @throws IllegalArgumentException if some property of the specified key
-     *         or value prevents it from being stored in this map
-     * @throws IllegalStateException implementations may, but are not
-     *         required to, throw this exception if the entry has been
-     *         removed from the backing map.
+     * specified replacement value is null, and this map does not permit null
+     * values
+     * @throws ClassCastException if a replacement value is of an inappropriate
+     *         type for this map
+     *         (<a href="Collection.html#optional-restrictions">optional</a>)
+     * @throws NullPointerException if function or a replacement value is null,
+     *         and this map does not permit null keys or values
+     *         (<a href="Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of a replacement value
+     *         prevents it from being stored in this map
+     *         (<a href="Collection.html#optional-restrictions">optional</a>)
+     * @throws ConcurrentModificationException if an entry is found to be
+     * removed during iteration
      * @since 1.8
      */
     default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
         Objects.requireNonNull(function);
         for (Map.Entry<K, V> entry : entrySet()) {
-            entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+            K k;
+            V v;
+            try {
+               k = entry.getKey();
+               v = entry.getValue();
+            } catch(IllegalStateException ise) {
+                throw new ConcurrentModificationException(ise);
+            }
+            entry.setValue(function.apply(k, v));
         }
     }
 
@@ -587,12 +610,10 @@
      * 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
-     * class overriding this method must specify its concurrency
-     * properties. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
+     * <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
@@ -607,43 +628,45 @@
      * @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
-     *         <tt>null</tt> if there was no mapping for the key.
-     *         (A <tt>null</tt> return can also indicate that the map
-     *         previously associated <tt>null</tt> with the key,
+     *         {@code 1} if there was no mapping for the key.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with the key,
      *         if the implementation supports null values.)
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
-     * @throws ClassCastException if the class of the specified key or value
-     *         prevents it from being stored in this map
+     * @throws ClassCastException if the key or value is of an inappropriate
+     *         type for this map
+     *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws NullPointerException if the specified key or value is null,
      *         and this map does not permit null keys or values
+     *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @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(null != put(key, value)) {
+        if (v == null) {
+            if (put(key, value) != null) {
                 throw new ConcurrentModificationException();
             }
-            return null;
-        } else {
-          return v;
         }
+
+        return v;
     }
 
     /**
      * 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
-     * class overriding this method must specify its concurrency
-     * properties. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
+     * <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}:
@@ -657,8 +680,8 @@
      *
      * @param key key with which the specified value is associated
      * @param value value expected to be associated with the specified key
-     * @return <tt>true</tt> if the value was removed
-     * @throws UnsupportedOperationException if the <tt>remove</tt> operation
+     * @return {@code true} if the value was removed
+     * @throws UnsupportedOperationException if the {@code remove} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the key or value is of an inappropriate
@@ -670,7 +693,7 @@
      * @since 1.8
      */
     default boolean remove(Object key, Object value) {
-        if (!containsKey(key) || !Objects.equals(get(key), value))
+        if (!Objects.equals(get(key), value) || !containsKey(key))
             return false;
         remove(key);
         return true;
@@ -680,13 +703,11 @@
      * 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
-     * class overriding this method must specify its concurrency
-     * properties. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
-     *
+     * <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}:
      *
@@ -700,8 +721,8 @@
      * @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
-     * @return <tt>true</tt> if the value was replaced
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
+     * @return {@code true} if the value was replaced
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of a specified key or value
@@ -723,12 +744,10 @@
      * 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
-     * class overriding this method must specify its concurrency
-     * properties. In particular, all implementations of
-     * subinterface {@link java.util.concurrent.ConcurrentMap}
-     * must ensure that this operation is performed atomically.
+     * <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}:
@@ -742,11 +761,11 @@
      * @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
-     *         <tt>null</tt> if there was no mapping for the key.
-     *         (A <tt>null</tt> return can also indicate that the map
-     *         previously associated <tt>null</tt> with the key,
+     *         {@code null} if there was no mapping for the key.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with the key,
      *         if the implementation supports null values.)
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of the specified key or value
@@ -777,16 +796,14 @@
      * <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
+     * <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.
+     * whether and how this method distinguishes absence from null mappings.
      *
      * @implSpec
      * The default implementation is equivalent to the following
@@ -807,7 +824,7 @@
      * @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
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of the specified key or value
@@ -823,25 +840,21 @@
     }
 
     /**
-     * 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.
+     * 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.
      *
-     * <p>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, and the current mapping
-     * is left unchanged.
+     * <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.
      *
-     * <p>The default implementation makes no guarantees about
-     * synchronization or atomicity properties of this method or the
-     * application of the remapping 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
+     * <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.
+     * whether and how this method distinguishes absence from null mappings.
      *
      * @implSpec
      * The default implementation is equivalent to performing the
@@ -867,7 +880,7 @@
      * @throws NullPointerException if the specified key is null and
      *         this map does not support null keys, or the
      *         remappingFunction is null
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of the specified key or value
@@ -877,17 +890,16 @@
      */
     default V computeIfPresent(K key,
                                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
-        V v;
-        while ((v = get(key)) != null) {
-            V newValue = remappingFunction.apply(key, v);
+        V oldValue;
+        while ((oldValue = get(key)) != null) {
+            V newValue = remappingFunction.apply(key, oldValue);
             if (newValue != null) {
-                if (replace(key, v, newValue))
+                if (replace(key, oldValue, newValue))
                     return newValue;
-            }
-            else if (remove(key, v))
+            } else if (remove(key, oldValue))
                 return null;
         }
-        return v;
+        return oldValue;
     }
 
     /**
@@ -905,28 +917,33 @@
      * (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 or the
-     * application of the remapping 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 exactly once atomically. Any class that
-     * permits null values must document whether and how this method
-     * distinguishes absence from null mappings.
+     * <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 {@code null} if absent:
+     * The default implementation is equivalent to performing the following
+     * steps for this {@code map}, then returning the current value or
+     * {@code null} if absent:
      *
-     * <pre> {@code
+     * <pre>{@code
      * V oldValue = map.get(key);
      * V newValue = remappingFunction.apply(key, oldValue);
-     * if (newValue != null)
-     *   map.replace(key, oldValue, newValue);
+     * if (oldValue != null )
+     *    if (newValue != null)
+     *       map.replace(key, oldValue, newValue);
+     *    else
+     *       map.remove(key, oldValue);
      * else
-     *   map.remove(key, oldValue);
+     *    if (newValue != null)
+     *       map.putIfAbsent(key, newValue);
+     *    else
+     *       return null;
      * }</pre>
      *
      * In concurrent contexts, the default implementation may retry
@@ -938,7 +955,7 @@
      * @throws NullPointerException if the specified key is null and
      *         this map does not support null keys, or the
      *         remappingFunction is null
-     * @throws UnsupportedOperationException if the <tt>put</tt> operation
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of the specified key or value
@@ -955,19 +972,17 @@
                 if (newValue != null) {
                     if (replace(key, oldValue, newValue))
                         return newValue;
-                }
-                else if (remove(key, oldValue)) {
+                } else if (remove(key, oldValue)) {
                     return null;
                 }
                 oldValue = get(key);
-            }
-            else {
+            } else {
                 if (newValue != null) {
                     if ((oldValue = putIfAbsent(key, newValue)) == null)
                         return newValue;
+                } else {
+                    return null;
                 }
-                else
-                    return null;
             }
         }
     }
@@ -978,7 +993,7 @@
      * Otherwise, replaces the value with the results of the given
      * remapping function, or removes if {@code null}. This method may
      * be of use when combining multiple mapped values for a key.  For
-     * example. to either create or append a {@code String msg} to a
+     * example, to either create or append a {@code String msg} to a
      * value mapping:
      *
      * <pre> {@code
@@ -989,15 +1004,14 @@
      * (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 or the
-     * application of the remapping 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 exactly once atomically. Any class that
-     * permits null values must document whether and how this method
-     * distinguishes absence from null mappings.
+     * <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
@@ -1023,7 +1037,7 @@
      * @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 <tt>put</tt> operation
+     * @throws UnsupportedOperationException if the {@code put} operation
      *         is not supported by this map
      *         (<a href="Collection.html#optional-restrictions">optional</a>)
      * @throws ClassCastException if the class of the specified key or value
@@ -1043,19 +1057,18 @@
                 if (newValue != null) {
                     if (replace(key, oldValue, newValue))
                         return newValue;
-                }
-                else if (remove(key, oldValue)) {
+                } else if (remove(key, oldValue)) {
                     return null;
                 }
                 oldValue = get(key);
-            }
-            else {
-                if (value != null) {
-                    if ((oldValue = putIfAbsent(key, value)) == null)
-                        return value;
+            } else {
+                if (value == null) {
+                    return null;
                 }
-                else
-                    return null;
+
+                if ((oldValue = putIfAbsent(key, value)) == null) {
+                    return value;
+                }
             }
         }
     }
--- a/src/share/classes/java/util/concurrent/ConcurrentMap.java	Thu Apr 11 15:53:17 2013 -0400
+++ b/src/share/classes/java/util/concurrent/ConcurrentMap.java	Thu Apr 11 12:54:30 2013 -0700
@@ -57,6 +57,21 @@
  * @param <V> the type of mapped values
  */
 public interface ConcurrentMap<K, V> extends Map<K, V> {
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implNote This implementation assumes that the ConcurrentMap cannot
+     * contain null values and get() returning null unambiguously means no
+     * mapping is present. Implementations which support null values must
+     * override this default implementation.
+     */
+    @Override
+    default V getOrDefault(Object key, V defaultValue) {
+        V v;
+        return (null != (v = get(key))) ? v : defaultValue;
+    }
+
     /**
      * If the specified key is not already associated
      * with a value, associate it with the given value.
@@ -72,11 +87,11 @@
      * @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
-     *         {@code null} if there was no mapping for the key.
-     *         (A {@code null} return can also indicate that the map
-     *         previously associated {@code null} with the key,
+     *         <tt>null</tt> if there was no mapping for the key.
+     *         (A <tt>null</tt> return can also indicate that the map
+     *         previously associated <tt>null</tt> with the key,
      *         if the implementation supports null values.)
-     * @throws UnsupportedOperationException if the {@code put} operation
+     * @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
--- a/test/java/util/Map/Defaults.java	Thu Apr 11 15:53:17 2013 -0400
+++ b/test/java/util/Map/Defaults.java	Thu Apr 11 12:54:30 2013 -0700
@@ -23,11 +23,13 @@
 
 /*
  * @test
- * @bug 8010122
+ * @bug 8010122 8004518
  * @summary Test Map default methods
  * @author Mike Duigou
  * @run testng Defaults
  */
+import java.util.AbstractMap;
+import java.util.AbstractSet;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -40,7 +42,9 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.function.Supplier;
@@ -274,6 +278,13 @@
         }), EXTRA_VALUE, description);
         assertTrue(map.containsKey(null));
         assertSame(map.get(null), EXTRA_VALUE, description);
+        assertSame(map.remove(null), EXTRA_VALUE, "removed value not expected");
+        assertFalse(map.containsKey(null), "null key present");
+        assertSame(map.compute(null, (k, v) -> {
+            assertSame(k, null);
+            assertNull(v);
+            return null;
+        }), null, description);
     }
 
     @Test(dataProvider = "R/W Map<IntegerEnum,String>")
@@ -448,7 +459,8 @@
             new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nulls)},
             new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nulls)},
             new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nulls), IntegerEnum.class, String.class)},
-            new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nulls))});
+            new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nulls))},
+            new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nulls)});
     }
 
     private static Collection<Object[]> makeRWNoNullsMaps() {
@@ -460,7 +472,8 @@
             new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false)},
             new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false)},
             new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false), IntegerEnum.class, String.class)},
-            new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false))});
+            new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false))},
+            new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false)});
     }
 
     private static Collection<Object[]> makeROMaps(boolean nulls) {
@@ -514,4 +527,68 @@
     public static <T> void assertInstance(T actual, Class<? extends T> expected, String message) {
         assertTrue(expected.isInstance(actual), message);
     }
+
+    /**
+     * A simple mutable map implementation that provides only default
+     * implementations of all methods. ie. none of the Map interface default
+     * methods have overridden implementations.
+     *
+     * @param <K> Type of keys
+     * @param <V> Type of values
+     */
+    public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K, V> {
+
+        protected final M map;
+
+        public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); }
+
+        protected ExtendsAbstractMap(M map) { this.map = map; }
+
+        public Set<Map.Entry<K, V>> entrySet() {
+            return new AbstractSet<Map.Entry<K, V>>() {
+                public int size() {
+                    return map.size();
+                }
+
+                public Iterator<Map.Entry<K,V>> iterator() {
+                    final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
+                    return new Iterator<Map.Entry<K,V>>() {
+                       public boolean hasNext() { return source.hasNext(); }
+                       public Map.Entry<K,V> next() { return source.next(); }
+                       public void remove() { source.remove(); }
+                    };
+                }
+
+                public boolean add(Map.Entry<K,V> e) {
+                    return map.entrySet().add(e);
+                }
+            };
+        }
+
+        public V put(K key, V value) {
+            return map.put(key, value);
+        }
+    }
+
+    /**
+     * A simple mutable concurrent map implementation that provides only default
+     * implementations of all methods. ie. none of the ConcurrentMap interface
+     * default methods have overridden implementations.
+     *
+     * @param <K> Type of keys
+     * @param <V> Type of values
+     */
+    public static class ImplementsConcurrentMap<K, V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
+        public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
+
+        // ConcurrentMap reabstracts these methods
+
+        public V replace(K k, V v) { return map.replace(k, v); };
+
+        public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };
+
+        public boolean remove(Object k, Object v) { return map.remove(k, v); }
+
+        public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }
+    }
 }