--- a/jdk/src/share/classes/java/util/Map.java Tue Apr 16 13:26:30 2013 +0100
+++ b/jdk/src/share/classes/java/util/Map.java Tue Apr 16 11:17:19 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,10 @@
package java.util;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
/**
* An object that maps keys to values. A map cannot contain duplicate keys;
* each key can map to at most one value.
@@ -475,4 +479,613 @@
*/
int hashCode();
+ // Defaultable methods
+
+ /**
+ * 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
+ * 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
+ * @throws ClassCastException if the key is of an inappropriate type for
+ * 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>)
+ */
+ default V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (((v = get(key)) != null) || containsKey(key))
+ ? v
+ : defaultValue;
+ }
+
+ /**
+ * Performs the given action on each entry in this map, in the order entries
+ * are returned by an entry set iterator (which may be unspecified), until
+ * all entries have been processed or the action throws an {@code Exception}.
+ * 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}:
+ * <pre> {@code
+ * for ((Map.Entry<K, V> entry : map.entrySet())
+ * action.accept(entry.getKey(), entry.getValue());
+ * }</pre>
+ *
+ * @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()) {
+ K k;
+ V v;
+ try {
+ k = entry.getKey();
+ v = entry.getValue();
+ } catch(IllegalStateException ise) {
+ throw new ConcurrentModificationException(ise);
+ }
+ action.accept(k, v);
+ }
+ }
+
+ /**
+ * 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 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
+ * for ((Map.Entry<K, V> entry : map.entrySet())
+ * entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+ * }</pre>
+ *
+ * @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.
+ * @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 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()) {
+ K k;
+ V v;
+ try {
+ k = entry.getKey();
+ v = entry.getValue();
+ } catch(IllegalStateException ise) {
+ throw new ConcurrentModificationException(ise);
+ }
+ entry.setValue(function.apply(k, v));
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a value (or is mapped
+ * 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);
+ * }</pre>
+ *
+ * @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 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 {@code put} 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
+ * 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 (put(key, value) != null) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ 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 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.containsKey(key) && Objects.equals(map.get(key), value)) {
+ * map.remove(key);
+ * return true;
+ * } else
+ * return false;
+ * }</pre>
+ *
+ * @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
+ * @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
+ * 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>)
+ * @since 1.8
+ */
+ default boolean remove(Object key, Object value) {
+ Object curValue = get(key);
+ if (!Objects.equals(curValue, value) ||
+ (curValue == null && !containsKey(key))) {
+ return false;
+ }
+ remove(key);
+ return true;
+ }
+
+ /**
+ * 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}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else
+ * return false;
+ * }</pre>
+ *
+ * @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 {@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
+ * prevents it from being stored in this map
+ * @throws NullPointerException if a specified key or value is null,
+ * and this map does not permit null keys or values
+ * @throws IllegalArgumentException if some property of a specified key
+ * or value prevents it from being stored in this map
+ * @since 1.8
+ */
+ default boolean replace(K key, V oldValue, V newValue) {
+ Object curValue = get(key);
+ if (!Objects.equals(curValue, oldValue) ||
+ (curValue == null && !containsKey(key))) {
+ return false;
+ }
+ put(key, newValue);
+ return true;
+ }
+
+ /**
+ * 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}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else
+ * return null;
+ * }</pre>
+ *
+ * @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
+ * {@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 {@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
+ * (<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
+ * @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;
+ }
+
+ /**
+ * 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
+ * exception is rethrown, and no mapping is recorded. The most
+ * common usage is to construct a new object serving as an initial
+ * mapped value or memoized result, as in:
+ *
+ * <pre> {@code
+ * map.computeIfAbsent(key, k -> new Value(f(k)));
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any 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>
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key is null and
+ * this map does not support null keys, or the
+ * mappingFunction is null
+ * @throws 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
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ V v, newValue;
+ return ((v = get(key)) == null &&
+ (newValue = mappingFunction.apply(key)) != null &&
+ (v = putIfAbsent(key, newValue)) == null) ? newValue : 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.
+ *
+ * <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. 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 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.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null and
+ * this map does not support null keys, or the
+ * remappingFunction is null
+ * @throws 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
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ V oldValue;
+ while ((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;
+ }
+ 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:
+ *
+ * <pre> {@code
+ * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
+ * (Method {@link #merge merge()} is often simpler to use for such purposes.)
+ *
+ * <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>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:
+ *
+ * <pre> {@code
+ * V oldValue = map.get(key);
+ * V newValue = remappingFunction.apply(key, oldValue);
+ * if (oldValue != null ) {
+ * if (newValue != null)
+ * map.replace(key, oldValue, newValue);
+ * else
+ * map.remove(key, oldValue);
+ * } else {
+ * if (newValue != null)
+ * map.putIfAbsent(key, newValue);
+ * 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
+ * remappingFunction is null
+ * @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
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ V oldValue = get(key);
+ for (;;) {
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (oldValue != null) {
+ if (newValue != null) {
+ if (replace(key, oldValue, newValue))
+ return newValue;
+ } else if (remove(key, oldValue)) {
+ return null;
+ }
+ oldValue = get(key);
+ } else {
+ if (newValue != null) {
+ if ((oldValue = putIfAbsent(key, newValue)) == null)
+ return newValue;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a value or is
+ * associated with null, associates it with the given value.
+ * Otherwise, replaces the value with the results of the given
+ * remapping function, or removes if the result is {@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
+ * value mapping:
+ *
+ * <pre> {@code
+ * map.merge(key, msg, String::concat)
+ * }</pre>
+ *
+ * <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>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:
+ *
+ * <pre> {@code
+ * V oldValue = map.get(key);
+ * V newValue = (oldValue == null) ? value :
+ * remappingFunction.apply(oldValue, value);
+ * if (newValue == null)
+ * map.remove(key, oldValue);
+ * else if (oldValue == null)
+ * 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
+ * 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
+ * (<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
+ * @since 1.8
+ */
+ default V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> 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) {
+ return null;
+ }
+
+ if ((oldValue = putIfAbsent(key, value)) == null) {
+ return value;
+ }
+ }
+ }
+ }
}