Is there a clean (and null safe) way to multiply the values of a map in Java?
My first instinct was to suggest a Stream
of the input Map
's entrySet
which maps the values to new values and terminates with collectors.toMap()
.
Unfortunately, Collectors.toMap
throws NullPointerException
when the value mapper function returns null
. Therefore it doesn't work with the null
values of your input Map
.
As an alternative, since you can't mutate your input Map
, I suggest that you create a copy of it and then call replaceAll
:
Map<String, Double> adjustedMap = new HashMap<>(someMap);
adjustedMap.replaceAll ((k,v) -> v != null ? 2*v : null);
As an alternative to streaming and/or copying solutions, the Maps.transformValues()
utility method exists in Google Guava:
Map<String, Double> adjustedMap = Maps.transformValues(someMap, value -> (value != null) ? (2 * value) : null);
This returns a lazy view of the original map that does not do any work on its own, but applies the given function when needed. This can be both a pro (if you're unlikely to ever need all the values, this will save you some computing time) and a con (if you'll need the same value many times, or if you need to further change someMap
without adjustedMap
seeing the changes) depending on your usage.
There already are many answers. Some of them seem a bit dubious to me. In any case, most of them inline the null
-check in one form or the other.
An approach that takes one step up the abstraction ladder is the following:
You want to apply an unary operator to the values of the map. So you can implement a method that applies an unary operator to the values of the map. (So far, so good). Now, you want a "special" unary operator that is null
-safe. Then, you can wrap a null
-safe unary operator around the original one.
This is shown here, with three different operators (one of them being Math::sin
, for that matter) :
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.UnaryOperator;
public class MapValueOps
{
public static void main(String[] args)
{
Map<String, Double> map = new LinkedHashMap<String, Double>();
map.put("A", 1.2);
map.put("B", 2.3);
map.put("C", null);
map.put("D", 4.5);
Map<String, Double> resultA = apply(map, nullSafe(d -> d * 2));
System.out.println(resultA);
Map<String, Double> resultB = apply(map, nullSafe(d -> d + 2));
System.out.println(resultB);
Map<String, Double> resultC = apply(map, nullSafe(Math::sin));
System.out.println(resultC);
}
private static <T> UnaryOperator<T> nullSafe(UnaryOperator<T> op)
{
return t -> (t == null ? t : op.apply(t));
}
private static <K> Map<K, Double> apply(
Map<K, Double> map, UnaryOperator<Double> op)
{
Map<K, Double> result = new LinkedHashMap<K, Double>();
for (Entry<K, Double> entry : map.entrySet())
{
result.put(entry.getKey(), op.apply(entry.getValue()));
}
return result;
}
}
I think this is clean, because it nicely separates the concerns of applying the operator and performing the null
-check. And it is null
-safe, because ... the method name says so.
(One could argue to pull the call to wrap the operator into a nullSafe
one into the apply
method, but that's not the point here)
Edit:
Depending on the intended application pattern, one could do something similar and apply the transformation in place, without creating a new map, by calling Map#replaceAll