src/share/classes/java/util/Map.java
Print this page
*** 23,32 ****
--- 23,36 ----
* questions.
*/
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.
*
* <p>This interface takes the place of the <tt>Dictionary</tt> class, which
*** 113,122 ****
--- 117,127 ----
* @see Collection
* @see Set
* @since 1.2
*/
public interface Map<K,V> {
+
// Query Operations
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
*** 473,478 ****
--- 478,933 ----
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
+ // Defaultable methods
+
+ /**
+ * Execute the specified {@code BiConsumer} with the key and value of
+ * each entry in this map.
+ *
+ * @param block the {@code BiConsumer} to which entries will be applied
+ */
+ default void forEach(BiConsumer<? super K, ? super V> block) {
+ Objects.requireNonNull(block);
+ for (Map.Entry<K, V> entry : entrySet()) {
+ block.accept(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Apply the specified function to each entry in this map, 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.
+ *
+ * @param function the function to apply to each entry
+ */
+ default void replaceAll(BiFunction<K, V, V> function) {
+ Objects.requireNonNull(function);
+ for (Map.Entry<K, V> entry : entrySet()) {
+ entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * associates it with the given value and returns {@code null},
+ * else returns the current value.
+ *
+ * <p>The default implementation is equivalent to, for this {@code
+ * map}:
+ *
+ * <pre> {@code
+ * if (!map.containsKey(key))
+ * return map.put(key, value);
+ * else
+ * return map.get(key);}</pre>
+ *
+ * <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.
+ *
+ * @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,
+ * if the implementation supports null values.)
+ * @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
+ * @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
+ */
+ default V putIfAbsent(K key, V value) {
+ return containsKey(key) ? get(key) : put(key, value);
+ }
+
+ /**
+ * Removes the entry for the specified key only if it is currently
+ * mapped to the specified value.
+ *
+ * <p>The default implementation is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(value)) {
+ * map.remove(key);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
+ * <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.
+ *
+ * @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
+ * is not supported by 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>)
+ */
+ default boolean remove(Object key, Object value) {
+ if (!containsKey(key) || !get(key).equals(value))
+ 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 is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
+ * <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.
+ *
+ * @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
+ * is not supported by this map
+ * @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
+ */
+ default boolean replace(K key, V oldValue, V newValue) {
+ if (!containsKey(key) || !get(key).equals(oldValue))
+ 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 is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else
+ * return null;}</pre>
+ *
+ * <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.
+ *
+ * @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,
+ * if the implementation supports null values.)
+ * @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
+ * @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
+ */
+ 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>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>
+ *
+ * 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 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
+ * present. Any class that permits null values must document
+ * whether and how this method distinguishes absence from null
+ * mappings.
+ *
+ * @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 <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
+ * @throws RuntimeException or Error if the mappingFunction does so,
+ * in which case the mapping is left unestablished
+ */
+ 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>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. 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 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
+ * present. Any class that permits null values must document
+ * whether and how this method distinguishes absence from null
+ * mappings.
+ *
+ * @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 <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
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ 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);
+ if (newValue != null) {
+ if (replace(key, v, newValue))
+ return newValue;
+ }
+ else if (remove(key, v))
+ return null;
+ }
+ return v;
+ }
+
+ /**
+ * 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} is often simpler to use for such purposes.)
+ *
+ * <p>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 (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. 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 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.
+ *
+ * @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 <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
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ default V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ for (;;) {
+ V oldValue = get(key);
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (newValue != null) {
+ if (replace(key, oldValue, newValue))
+ return newValue;
+ }
+ else if (remove(key, oldValue))
+ return null;
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a
+ * (non-null) value, associates it with the given value.
+ * 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
+ * value mapping:
+ *
+ * <pre> {@code
+ * map.merge(key, msg, String::concat)}</pre>
+ *
+ * <p>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. 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 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.
+ *
+ * @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 NullPointerException if the specified key is null and
+ * this map does not support null keys, or the
+ * remappingFunction is null
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ default V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ for (;;) {
+ V oldValue, newValue;
+ if ((oldValue = get(key)) == null) {
+ if (value == null || putIfAbsent(key, value) == null)
+ return value;
+ }
+ else if ((newValue = remappingFunction.apply(oldValue, value)) != null) {
+ if (replace(key, oldValue, newValue))
+ return newValue;
+ }
+ else if (remove(key, oldValue))
+ return null;
+ }
+ }
}