Here are some variations on Answer from Sotirios Delimanolis , which was good enough to start with (+1). Consider the following:
static Map transform(Map extends X, ? extends Y> input,
Function function) {
return input.keySet().stream()
.collect(Collectors.toMap(Function.identity(),
key -> function.apply(input.get(key))));
}
A couple of points here. The first is the use of wildcards in generics; this makes the function a little more flexible. A wildcard would be needed if, for example, you want the output map to have a key that is a superclass of the input map key:
Map input = new HashMap();
input.put("string1", "42");
input.put("string2", "41");
Map output = transform(input, Integer::parseInt);
(There is also an example for map values, but it’s really made up, and I admit having the limited wildcard for Y only helps in Edge cases.)
A second point is that instead of streaming on entrySet
of the input map, I ran it on keySet
. This makes the code a little cleaner, I believe, at the cost of having to retrieve the values from the map instead of the map entry. Incidentally, I initially had key -> key
as the first argument for toMap()
and this failed with a type inference error for some reason. The modification a (X key) -> key
it worked, as well as Function.identity()
.
Yet another variation is the following:
static Map transform1(Map extends X, ? extends Y> input,
Function function) {
Map result = new HashMap<>();
input.forEach((k, v) -> result.put(k, function.apply(v)));
return result;
}
This uses Map.forEach()
instead of streams. This is even easier, I think, because it dispenses with collectors, who are quite clunky to use with maps. The reason is that Map.forEach()
provides the key and value as separate parameters, while the stream has only one value – and you have to choose whether to use the key or the map entry as that value. On the downside, this one lacks the rich, fluid goodness of the other approaches. 🙂