aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/java/util/concurrent
diff options
context:
space:
mode:
authormduigou <none@none>2013-10-23 14:32:41 -0700
committermduigou <none@none>2013-10-23 14:32:41 -0700
commit76c7e1e727a6a5adfb5f040e677dcf1860358a6c (patch)
tree1f2361e2a52c290d6478971c93791124abe284e5 /src/share/classes/java/util/concurrent
parent66f793398494fba5b4ceffde8e8ddb6a3493ffc2 (diff)
downloadjdk8u_jdk-76c7e1e727a6a5adfb5f040e677dcf1860358a6c.tar.gz
8024688: further split Map and ConcurrentMap defaults eliminating looping from Map defaults, Map.merge fixes and doc fixes.
Reviewed-by: psandoz, dholmes
Diffstat (limited to 'src/share/classes/java/util/concurrent')
-rw-r--r--src/share/classes/java/util/concurrent/ConcurrentMap.java335
1 files changed, 322 insertions, 13 deletions
diff --git a/src/share/classes/java/util/concurrent/ConcurrentMap.java b/src/share/classes/java/util/concurrent/ConcurrentMap.java
index 6c902fa686..3cb1fe7ea8 100644
--- a/src/share/classes/java/util/concurrent/ConcurrentMap.java
+++ b/src/share/classes/java/util/concurrent/ConcurrentMap.java
@@ -36,7 +36,9 @@
package java.util.concurrent;
import java.util.Map;
import java.util.Objects;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
+import java.util.function.Function;
/**
* A {@link java.util.Map} providing thread safety and atomicity
@@ -64,9 +66,13 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* {@inheritDoc}
*
* @implNote This implementation assumes that the ConcurrentMap cannot
- * contain null values and get() returning null unambiguously means the key
- * is absent. Implementations which support null values must override this
- * default implementation.
+ * contain null values and {@code get()} returning null unambiguously means
+ * the key is absent. Implementations which support null values
+ * <strong>must</strong> override this default implementation.
+ *
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
*/
@Override
default V getOrDefault(Object key, V defaultValue) {
@@ -74,6 +80,41 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
return ((v = get(key)) != null) ? v : defaultValue;
}
+ /**
+ * {@inheritDoc}
+ *
+ * @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>
+ *
+ * @implNote The default implementation assumes that
+ * {@code IllegalStateException} thrown by {@code getKey()} or
+ * {@code getValue()} indicates that the entry has been removed and cannot
+ * be processed. Operation continues for subsequent entries.
+ *
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ 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) {
+ // this usually means the entry is no longer in the map.
+ continue;
+ }
+ action.accept(k, v);
+ }
+ }
+
/**
* If the specified key is not already associated
* with a value, associate it with the given value.
@@ -82,10 +123,14 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* if (!map.containsKey(key))
* return map.put(key, value);
* else
- * return map.get(key);}</pre>
+ * return map.get(key);
+ * }</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@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
@@ -102,7 +147,7 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* @throws IllegalArgumentException if some property of the specified key
* or value prevents it from being stored in this map
*/
- V putIfAbsent(K key, V value);
+ V putIfAbsent(K key, V value);
/**
* Removes the entry for a key only if currently mapped to a given value.
@@ -112,10 +157,14 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* map.remove(key);
* return true;
* } else
- * return false;}</pre>
+ * return false;
+ * }</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@code Map}.
+ *
* @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
@@ -138,10 +187,14 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* map.put(key, newValue);
* return true;
* } else
- * return false;}</pre>
+ * return false;
+ * }</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@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
@@ -164,10 +217,14 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* if (map.containsKey(key)) {
* return map.put(key, value);
* } else
- * return null;}</pre>
+ * return null;
+ * }</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@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
@@ -189,10 +246,30 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
/**
* {@inheritDoc}
*
- * @implNote This implementation assumes that the ConcurrentMap cannot
- * contain null values and get() returning null unambiguously means the key
- * is absent. Implementations which support null values
- * <strong>must</strong> override this default implementation.
+ * @implSpec
+ * <p>The default implementation is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * for ((Map.Entry<K, V> entry : map.entrySet())
+ * do {
+ * K k = entry.getKey();
+ * V v = entry.getValue();
+ * } while(!replace(k, v, function.apply(k, v)));
+ * }</pre>
+ *
+ * The default implementation may retry these steps when multiple
+ * threads attempt updates including potentially calling the function
+ * repeatedly for a given key.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
*/
@Override
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
@@ -200,11 +277,243 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
forEach((k,v) -> {
while(!replace(k, v, function.apply(k, v))) {
// v changed or k is gone
- if( (v = get(k)) == null) {
+ if ( (v = get(k)) == null) {
// k is no longer in the map.
break;
}
}
});
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @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)
+ * return map.putIfAbsent(key, newValue);
+ * }
+ * }</pre>
+ *
+ * The default implementation may retry these steps when multiple
+ * threads attempt updates including potentially calling the mapping
+ * function multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ 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;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @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>
+ *
+ * The default implementation may retry these steps when multiple threads
+ * attempt updates including potentially calling the remapping function
+ * multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(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;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @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>
+ *
+ * The default implementation may retry these steps when multiple
+ * threads attempt updates including potentially calling the remapping
+ * function multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V compute(K key,
+ 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;
+ }
+ } 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.
+ }
+ }
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @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);
+ * else if (oldValue == null)
+ * map.remove(key);
+ * else
+ * map.put(key, newValue);
+ * }</pre>
+ *
+ * <p>The default implementation may retry these steps when multiple
+ * threads attempt updates including potentially calling the remapping
+ * function multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ Objects.requireNonNull(value);
+ 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 ((oldValue = putIfAbsent(key, value)) == null) {
+ return value;
+ }
+ }
+ }
+ }
}