HashMap for HashMap using Stream / Map-Reduce / Collector

Here are some variations on Answer from Sotirios Delimanolis , which was good enough to start with (+1). Consider the following:

static <X, Y, Z> Map<X, Z> transform(Map<? extends X, ? extends Y> input,
                                     Function<Y, Z> 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<String, String> input = new HashMap<String, String>();
input.put("string1", "42");
input.put("string2", "41");
Map<CharSequence, Integer> 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 <X, Y, Z> Map<X, Z> transform1(Map<? extends X, ? extends Y> input,
                                      Function<Y, Z> function) {
    Map<X, Z> 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. 🙂

Leave a comment