--- 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;
}
}
}