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; } }