src/share/classes/java/util/Map.java

Print this page
rev 8834 : 8029055: Map.merge and ConcurrentMap.merge @implSpec is not correct in case of non-existing key-value pair or a null value entry
Reviewed-by: duke


1120      * <pre> {@code
1121      * map.merge(key, msg, String::concat)
1122      * }</pre>
1123      *
1124      * <p>If the function returns {@code null}, the mapping is removed (or
1125      * remains absent if initially absent).  If the function itself throws an
1126      * (unchecked) exception, the exception is rethrown, and the current mapping
1127      * is left unchanged.
1128      *
1129      * @implSpec
1130      * The default implementation is equivalent to performing the
1131      * following steps for this {@code map}, then returning the
1132      * current value or {@code null} if absent:
1133      *
1134      * <pre> {@code
1135      * V oldValue = map.get(key);
1136      * V newValue = (oldValue == null) ? value :
1137      *              remappingFunction.apply(oldValue, value);
1138      * if (newValue == null)
1139      *     map.remove(key);
1140      * else if (oldValue == null)
1141      *     map.remove(key);
1142      * else
1143      *     map.put(key, newValue);
1144      * }</pre>
1145      *
1146      * <p>The default implementation makes no guarantees about synchronization
1147      * or atomicity properties of this method. Any implementation providing
1148      * atomicity guarantees must override this method and document its
1149      * concurrency properties. In particular, all implementations of
1150      * subinterface {@link java.util.concurrent.ConcurrentMap} must document
1151      * whether the function is applied once atomically only if the value is not
1152      * present.
1153      *
1154      * @param key key with which the specified value is to be associated
1155      * @param value the value to use if absent
1156      * @param remappingFunction the function to recompute a value if present
1157      * @return the new value associated with the specified key, or null if none
1158      * @throws UnsupportedOperationException if the {@code put} operation
1159      *         is not supported by this map
1160      *         (<a href="Collection.html#optional-restrictions">optional</a>)
1161      * @throws ClassCastException if the class of the specified key or value
1162      *         prevents it from being stored in this map
1163      *         (<a href="Collection.html#optional-restrictions">optional</a>)
1164      * @throws NullPointerException if the specified key is null and
1165      *         this map does not support null keys, or the remappingFunction
1166      *         is null
1167      * @since 1.8
1168      */
1169     default V merge(K key, V value,
1170             BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
1171         Objects.requireNonNull(remappingFunction);
1172         V oldValue = get(key);
1173         if (oldValue != null) {
1174             V newValue = remappingFunction.apply(oldValue, value);
1175             if (newValue != null) {
1176                 put(key, newValue);
1177                 return newValue;
1178             } else {
1179                 remove(key);
1180                 return null;
1181             }
1182         } else {
1183             if (value == null) {
1184                 remove(key);
1185                 return null;
1186             } else {
1187                 put(key, value);
1188                 return value;
1189             }
1190         }

1191     }
1192 }


1120      * <pre> {@code
1121      * map.merge(key, msg, String::concat)
1122      * }</pre>
1123      *
1124      * <p>If the function returns {@code null}, the mapping is removed (or
1125      * remains absent if initially absent).  If the function itself throws an
1126      * (unchecked) exception, the exception is rethrown, and the current mapping
1127      * is left unchanged.
1128      *
1129      * @implSpec
1130      * The default implementation is equivalent to performing the
1131      * following steps for this {@code map}, then returning the
1132      * current value or {@code null} if absent:
1133      *
1134      * <pre> {@code
1135      * V oldValue = map.get(key);
1136      * V newValue = (oldValue == null) ? value :
1137      *              remappingFunction.apply(oldValue, value);
1138      * if (newValue == null)
1139      *     map.remove(key);


1140      * else
1141      *     map.put(key, newValue);
1142      * }</pre>
1143      *
1144      * <p>The default implementation makes no guarantees about synchronization
1145      * or atomicity properties of this method. Any implementation providing
1146      * atomicity guarantees must override this method and document its
1147      * concurrency properties. In particular, all implementations of
1148      * subinterface {@link java.util.concurrent.ConcurrentMap} must document
1149      * whether the function is applied once atomically only if the value is not
1150      * present.
1151      *
1152      * @param key key with which the specified value is to be associated
1153      * @param value the value to use if absent
1154      * @param remappingFunction the function to recompute a value if present
1155      * @return the new value associated with the specified key, or null if none
1156      * @throws UnsupportedOperationException if the {@code put} operation
1157      *         is not supported by this map
1158      *         (<a href="Collection.html#optional-restrictions">optional</a>)
1159      * @throws ClassCastException if the class of the specified key or value
1160      *         prevents it from being stored in this map
1161      *         (<a href="Collection.html#optional-restrictions">optional</a>)
1162      * @throws NullPointerException if the specified key is null and
1163      *         this map does not support null keys, or the remappingFunction
1164      *         is null
1165      * @since 1.8
1166      */
1167     default V merge(K key, V value,
1168             BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
1169         Objects.requireNonNull(remappingFunction);
1170         V oldValue = get(key);
1171         V newValue = (oldValue == null) ? value :
1172                    remappingFunction.apply(oldValue, value);
1173         if(newValue == null) {



1174             remove(key);


1175         } else {
1176             put(key, newValue);






1177         }
1178         return newValue;
1179     }
1180 }