map and null values
Peter Levart
peter.levart at gmail.com
Thu Jan 3 02:41:35 PST 2013
On 01/02/2013 06:01 PM, Brian Goetz wrote:
>> >Also the assumption the null values are not legal is so strong, that the
>> >implementation of replace::
>> >
>> > default boolean replace(K key, V oldValue, V newValue) {
>> > if (*!containsKey(key) || !get(key).equals(oldValue)*)
>> > return false;
>> > put(key, newValue);
>> > return true;
>> > }
>> >
>> >throws NPE exception in the example below:
>> >
>> >mss.put("null", null);
>> >
>> >mss.replace("null", null, "null");
> Note that many existing Map implementations throw NPE when you try and
> put a null key or value into it anyway.
>
>
That's true, but the default *replace* method could do better for those
(3rd party) Maps that allow null values. For example:
default boolean replace(K key, V oldValue, V newValue) {
if (!containsKey(key) || !Objects.equals(get(key), oldValue))
return false;
put(key, newValue);
return true;
}
The same with default *remove* method:
default boolean remove(Object key, Object value) {
if (!containsKey(key) || !Objects.equals(get(key), value))
return false;
remove(key);
return true;
}
Also the default *compute* methods doesn't play well:
Map<Integer, String> map = new HashMap<>();
map.compute(1, (k, v) -> "x");
...loops infinitely and:
Map<Integer, String> map = new ConcurrentHashMap<>();
map.compute(1, (k, v) -> "x");
...throws NPE.
I think the following implementation is better:
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V>
remappingFunction) {
V oldValue = get(key);
for (;;) {
if (oldValue != null || containsKey(key)) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
if (replace(key, oldValue, newValue))
return newValue;
}
else if (remove(key, oldValue)) {
return null;
}
oldValue = get(key);
}
else {
V newValue = remappingFunction.apply(key, null);
if (newValue != null) {
if ((oldValue = putIfAbsent(key, newValue)) == null)
return newValue;
}
else
return null;
}
}
}
Also, the default *merge* method is not good:
Map<Integer, String> map = new HashMap<>();
map.put(1, null);
String newVal = map.merge(1, "x", (old, val) -> "y");
System.out.println(newVal);
System.out.println(map);
...prints:
x
{1=null}
The better variant is:
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V>
remappingFunction) {
V oldValue = get(key);
for (;;) {
if (oldValue != null || containsKey(key)) {
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 (value != null) {
if ((oldValue = putIfAbsent(key, value)) == null)
return value;
}
else
return null;
}
}
}
Will HashMap get optimized implementations? Accepting contributions?
Regards, Peter
More information about the lambda-dev
mailing list