# HG changeset patch
# User mduigou
# Date 1365443864 25200
# Node ID 7a4c4553870b8a492e6af71a9bdbd69b5be7fa68
# Parent b702977e7212d2d8ed16c5f4e321a9f2e6227ffb
8004518: Add in-place operations to Map
8010122: Add atomic operations to Map
Reviewed-by: darcy, briangoetz, mduigou
Contributed-by: Doug Lea
, Henry Jen , Akhil Arora , Peter Levart
diff --git a/src/share/classes/java/util/Collections.java b/src/share/classes/java/util/Collections.java
--- a/src/share/classes/java/util/Collections.java
+++ b/src/share/classes/java/util/Collections.java
@@ -28,6 +28,9 @@
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
/**
* This class consists exclusively of static methods that operate on or return
@@ -1391,6 +1394,50 @@
public int hashCode() {return m.hashCode();}
public String toString() {return m.toString();}
+ // Override default methods in Map
+ @Override
+ public V putIfAbsent(K key, V value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V computeIfPresent(K key,
+ BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V compute(K key,
+ BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V merge(K key, V value,
+ BiFunction super V, ? super V, ? extends V> remappingFunction) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* We need this class in addition to UnmodifiableSet as
* Map.Entries themselves permit modification of the backing Map
@@ -1590,9 +1637,9 @@
*
* Failure to follow this advice may result in non-deterministic behavior.
*
- *
The returned collection does not pass the hashCode
- * and equals operations through to the backing collection, but
- * relies on Object's equals and hashCode methods. This is
+ *
The returned collection does not pass the {@code hashCode}
+ * and {@code equals} operations through to the backing collection, but
+ * relies on {@code Object}'s equals and hashCode methods. This is
* necessary to preserve the contracts of these operations in the case
* that the backing collection is a set or a list.
*
@@ -2107,6 +2154,50 @@
public String toString() {
synchronized (mutex) {return m.toString();}
}
+
+ // Override default methods in Map
+ @Override
+ 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 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);}
+ }
+
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
diff --git a/src/share/classes/java/util/HashMap.java b/src/share/classes/java/util/HashMap.java
--- a/src/share/classes/java/util/HashMap.java
+++ b/src/share/classes/java/util/HashMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, 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
@@ -24,7 +24,11 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.Consumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
/**
* Hash table based implementation of the Map interface. This
@@ -350,6 +354,13 @@
return null == entry ? null : entry.getValue();
}
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ Entry entry = getEntry(key);
+
+ return null == entry ? defaultValue : entry.getValue();
+ }
+
/**
* Returns true if this map contains a mapping for the
* specified key.
@@ -567,6 +578,238 @@
return (e == null ? null : e.value);
}
+ // optimized implementations of default methods in Map
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry)table[i];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ if(e.value != null) {
+ return e.value;
+ }
+ e.value = value;
+ modCount++;
+ e.recordAccess(this);
+ return null;
+ }
+ }
+
+ modCount++;
+ addEntry(hash, key, value, i);
+ return null;
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry prev = (Entry)table[i];
+ Entry e = prev;
+
+ while (e != null) {
+ Entry next = e.next;
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ if(!Objects.equals(e.value, value)) {
+ return false;
+ }
+ modCount++;
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ return true;
+ }
+ prev = e;
+ e = next;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry)table[i];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key) && Objects.equals(e.value, oldValue)) {
+ e.value = newValue;
+ e.recordAccess(this);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry)table[i];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V oldValue = e.value;
+ e.value = value;
+ e.recordAccess(this);
+ return oldValue;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry)table[i];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V oldValue = e.value;
+ return oldValue == null ? (e.value = mappingFunction.apply(key)) : oldValue;
+ }
+ }
+
+ V newValue = mappingFunction.apply(key);
+ if (newValue != null) {
+ modCount++;
+ addEntry(hash, key, newValue, i);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry prev = (Entry)table[i];
+ Entry e = prev;
+
+ while (e != null) {
+ Entry next = e.next;
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V oldValue = e.value;
+ if (oldValue == null)
+ break;
+ V newValue = remappingFunction.apply(key, oldValue);
+ modCount++;
+ if (newValue == null) {
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ }
+ else {
+ e.value = newValue;
+ e.recordAccess(this);
+ }
+ return newValue;
+ }
+ prev = e;
+ e = next;
+ }
+
+ return null;
+ }
+
+ @Override
+ public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry prev = (Entry)table[i];
+ Entry e = prev;
+
+ while (e != null) {
+ Entry next = e.next;
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V oldValue = e.value;
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (newValue != oldValue) {
+ modCount++;
+ if (newValue == null) {
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ } else {
+ e.value = newValue;
+ e.recordAccess(this);
+ }
+ }
+ return newValue;
+ }
+ prev = e;
+ e = next;
+ }
+
+ V newValue = remappingFunction.apply(key, null);
+ if (newValue != null) {
+ modCount++;
+ addEntry(hash, key, newValue, i);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
+ int hash = (key == null) ? 0 : hash(key);
+ int i = indexFor(hash, table.length);
+ @SuppressWarnings("unchecked")
+ Entry prev = (Entry)table[i];
+ Entry e = prev;
+
+ while (e != null) {
+ Entry next = e.next;
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V oldValue = e.value;
+ V newValue = remappingFunction.apply(oldValue, value);
+ modCount++;
+ if (newValue == null) {
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ } else {
+ e.value = newValue;
+ e.recordAccess(this);
+ }
+ return newValue;
+ }
+ prev = e;
+ e = next;
+ }
+
+ if (value != null) {
+ modCount++;
+ addEntry(hash, key, value, i);
+ }
+
+ return value;
+ }
+
+ // end of optimized implementations of default methods in Map
+
/**
* Removes and returns the entry associated with the specified key
* in the HashMap. Returns null if the HashMap contains no mapping
diff --git a/src/share/classes/java/util/Hashtable.java b/src/share/classes/java/util/Hashtable.java
--- a/src/share/classes/java/util/Hashtable.java
+++ b/src/share/classes/java/util/Hashtable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 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
@@ -24,7 +24,10 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.Function;
+import java.util.function.BiFunction;
/**
* This class implements a hash table, which maps keys to values. Any
@@ -455,6 +458,26 @@
}
}
+ private void addEntry(int hash, K key, V value, int index) {
+ modCount++;
+
+ Entry,?> tab[] = table;
+ if (count >= threshold) {
+ // Rehash the table if the threshold is exceeded
+ rehash();
+
+ tab = table;
+ hash = hash(key);
+ index = (hash & 0x7FFFFFFF) % tab.length;
+ }
+
+ // Creates the new entry.
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ tab[index] = new Entry<>(hash, key, value, e);
+ count++;
+ }
+
/**
* Maps the specified key to the specified
* value in this hashtable. Neither the key nor the
@@ -492,21 +515,7 @@
}
}
- modCount++;
- if (count >= threshold) {
- // Rehash the table if the threshold is exceeded
- rehash();
-
- tab = table;
- hash = hash(key);
- index = (hash & 0x7FFFFFFF) % tab.length;
- }
-
- // Creates the new entry.
- @SuppressWarnings("unchecked")
- Entry e = (Entry)tab[index];
- tab[index] = new Entry<>(hash, key, value, e);
- count++;
+ addEntry(hash, key, value, index);
return null;
}
@@ -892,6 +901,227 @@
return h;
}
+ @Override
+ public synchronized V getOrDefault(Object key, V defaultValue) {
+ V result = get(key);
+ return (null == result) ? defaultValue : result;
+ }
+
+ @Override
+ public synchronized void replaceAll(
+ BiFunction super K, ? super V, ? extends V> function)
+ {
+ Map.super.replaceAll(function);
+ }
+
+ @Override
+ public synchronized V putIfAbsent(K key, V value) {
+ Objects.requireNonNull(value);
+
+ // Makes sure the key is not already in the hashtable.
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry entry = (Entry)tab[index];
+ for(; entry != null ; entry = entry.next) {
+ if ((entry.hash == hash) && entry.key.equals(key)) {
+ V old = entry.value;
+ if (old == null) {
+ entry.value = value;
+ }
+ return old;
+ }
+ }
+
+ addEntry(hash, key, value, index);
+ return null;
+ }
+
+ @Override
+ public synchronized boolean remove(Object key, Object value) {
+ Objects.requireNonNull(value);
+
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry)tab[index];
+ for(Entry prev = null ; e != null ; prev = e, e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ e.value = null;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized boolean replace(K key, V oldValue, V newValue) {
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for (; e != null ; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ if (e.value.equals(oldValue)) {
+ e.value = newValue;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized V replace(K key, V value) {
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for (; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ V oldValue = e.value;
+ e.value = value;
+ return oldValue;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ // Hashtable not accept null value
+ return e.value;
+ }
+ }
+
+ V newValue = mappingFunction.apply(key);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for(Entry prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for(Entry prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ V newValue = remappingFunction.apply(key, null);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) tab[index];
+ for(Entry prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(e.value, value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ if (value != null) {
+ addEntry(hash, key, value, index);
+ }
+
+ return value;
+ }
+
/**
* Save the state of the Hashtable to a stream (i.e., serialize it).
*
diff --git a/src/share/classes/java/util/Map.java b/src/share/classes/java/util/Map.java
--- a/src/share/classes/java/util/Map.java
+++ b/src/share/classes/java/util/Map.java
@@ -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,584 @@
*/
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.
+ *
+ * @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
+ * (optional)
+ * @throws NullPointerException if the specified key is null and this map
+ * does not permit null keys
+ * (optional)
+ */
+ default V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (null != (v = get(key)))
+ ? v
+ : containsKey(key) ? null : defaultValue;
+ }
+
+ /**
+ * Performs the given action on each entry in this map, in the
+ * order entries are returned by an entry set iterator, until all entries
+ * have been processed or the action throws an {@code Exception}.
+ * Exceptions thrown by the action are relayed to the caller.
+ *
+ *
The default implementation should be overridden by implementations if
+ * they can provide a more performant implementation than an iterator-based
+ * one.
+ *
+ *
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.
+ *
+ * @implSpec
+ *
The default implementation is equivalent to, for this {@code map}:
+ *
+ *
+ * @param action The action to be performed for each entry
+ * @throws NullPointerException if the specified action is null
+ * @since 1.8
+ */
+ default void forEach(BiConsumer super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ for (Map.Entry entry : entrySet()) {
+ action.accept(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * 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.
+ *
+ *
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.
+ *
+ * @implSpec
+ *
The default implementation is equivalent to, for this {@code map}:
+ *
+ *
+ * @param function the function to apply to each entry
+ * @throws UnsupportedOperationException if the put 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 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.
+ * @since 1.8
+ */
+ default void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
+ Objects.requireNonNull(function);
+ for (Map.Entry entry : entrySet()) {
+ entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+ }
+ }
+
+ /**
+ * 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.
+ *
+ *
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.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code
+ * map}:
+ *
+ *
+ *
+ * @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
+ * null if there was no mapping for the key.
+ * (A null return can also indicate that the map
+ * previously associated null with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the put operation
+ * is not supported by this map
+ * (optional)
+ * @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
+ * @since 1.8
+ */
+ default V putIfAbsent(K key, V value) {
+ V v = get(key);
+ if(v == null) {
+ if(null != put(key, value)) {
+ throw new ConcurrentModificationException();
+ }
+ return null;
+ } else {
+ return v;
+ }
+ }
+
+ /**
+ * Removes the entry for the specified key only if it is currently
+ * mapped to the specified value.
+ *
+ *
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.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ *
+ *
+ * @param key key with which the specified value is associated
+ * @param value value expected to be associated with the specified key
+ * @return true if the value was removed
+ * @throws UnsupportedOperationException if the remove operation
+ * is not supported by this map
+ * (optional)
+ * @throws ClassCastException if the key or value is of an inappropriate
+ * type for this map
+ * (optional)
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * (optional)
+ * @since 1.8
+ */
+ default boolean remove(Object key, Object value) {
+ if (!containsKey(key) || !Objects.equals(get(key), value))
+ return false;
+ remove(key);
+ return true;
+ }
+
+ /**
+ * Replaces the entry for the specified key only if currently
+ * mapped to the specified value.
+ *
+ *
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.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ *
+ *
+ * @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 true if the value was replaced
+ * @throws UnsupportedOperationException if the put operation
+ * is not supported by this map
+ * (optional)
+ * @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) {
+ if (!containsKey(key) || !Objects.equals(get(key), oldValue))
+ return false;
+ put(key, newValue);
+ return true;
+ }
+
+ /**
+ * Replaces the entry for the specified key only if it is
+ * currently mapped to some value.
+ *
+ *
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.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ *
+ *
+ * @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
+ * null if there was no mapping for the key.
+ * (A null return can also indicate that the map
+ * previously associated null with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the put operation
+ * is not supported by this map
+ * (optional)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (optional)
+ * @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}.
+ *
+ *
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:
+ *
+ *
{@code
+ * map.computeIfAbsent(key, k -> new Value(f(k)));}
+ *
+ *
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.
+ *
+ * @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:
+ *
+ *
{@code
+ * if (map.get(key) == null) {
+ * V newValue = mappingFunction.apply(key);
+ * if (newValue != null)
+ * map.putIfAbsent(key, newValue);
+ * }}
+ *
+ * @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 put operation
+ * is not supported by this map
+ * (optional)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (optional)
+ * @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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ * @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:
+ *
+ *
+ *
+ * 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 put operation
+ * is not supported by this map
+ * (optional)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (optional)
+ * @since 1.8
+ */
+ 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:
+ *
+ *
+ * (Method {@link #merge} is often simpler to use for such purposes.)
+ *
+ *
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.
+ *
+ *
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.
+ *
+ * @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:
+ *
+ *
+ *
+ * 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 put operation
+ * is not supported by this map
+ * (optional)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (optional)
+ * @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
+ * (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:
+ *
+ *
{@code
+ * map.merge(key, msg, String::concat)}
+ *
+ *
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.
+ *
+ *
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.
+ *
+ * @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:
+ *
+ *