src/share/classes/java/util/Map.java
Print this page
rev 8834 : 8029055: Map.merge implementations should refuse null value param
Reviewed-by: duke
*** 1108,1146 ****
}
}
/**
* 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.
*
* @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 makes no guarantees about synchronization
--- 1108,1143 ----
}
}
/**
* If the specified key is not already associated with a value or is
! * associated with null, associates it with the given non-null value.
! * Otherwise, replaces the associated 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. If the
! * function itself throws an (unchecked) exception, the exception is
! * rethrown, and the current mapping is left unchanged.
*
* @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
* map.put(key, newValue);
* }</pre>
*
* <p>The default implementation makes no guarantees about synchronization
*** 1149,1192 ****
* 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.
*
! * @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) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
! if (oldValue != null) {
! V newValue = remappingFunction.apply(oldValue, value);
! if (newValue != null) {
! put(key, newValue);
! return newValue;
! } else {
! remove(key);
! return null;
! }
! } else {
! if (value == null) {
remove(key);
- return null;
} else {
! put(key, value);
! return value;
! }
}
}
}
--- 1146,1183 ----
* 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.
*
! * @param key key with which the resulting value is to be associated
! * @param value the non-null value to be merged with the existing value
! * associated with the key or, if no existing value or a null value
! * is associated with the key, to be associated with the key
* @param remappingFunction the function to recompute a value if present
! * @return the new value associated with the specified key, or null if no
! * value is associated with the key
* @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 support null keys or values, or the
! * remappingFunction is null
* @since 1.8
*/
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);
! V newValue = (oldValue == null) ? value :
! remappingFunction.apply(oldValue, value);
! if(newValue == null) {
remove(key);
} else {
! put(key, newValue);
}
+ return newValue;
}
}