diff options
author | mduigou <none@none> | 2013-10-23 14:32:41 -0700 |
---|---|---|
committer | mduigou <none@none> | 2013-10-23 14:32:41 -0700 |
commit | 76c7e1e727a6a5adfb5f040e677dcf1860358a6c (patch) | |
tree | 1f2361e2a52c290d6478971c93791124abe284e5 /src/share/classes/java/util/concurrent | |
parent | 66f793398494fba5b4ceffde8e8ddb6a3493ffc2 (diff) | |
download | jdk8u_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.java | 335 |
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; + } + } + } + } } |