Is it possible to merge resp. simplify these two Collectors.toMap calls?

53 Views Asked by At

Please consider the follwing exemplifying code:

Function<Pair<String, String>, Integer> lengthOfKey
        = p -> p.getKey().length();

Collector<Pair<String, String>, ?, Map<String, String>>
        convertPairListToMap =
        Collectors.toMap(
            Pair<String, String>::getKey,
            Pair<String, String>::getValue);

Function<Map<String, String>, Map<String, String>>
        convertValueToUpperCase =
        map -> map.entrySet().stream().collect(
                Collectors.toMap(
                        Map.Entry::getValue,
                        e -> e.getValue().toUpperCase())
        );

list.stream()
        .collect(groupingBy(
                lengthOfKey,
                collectingAndThen(
                    convertPairListToMap,
                    convertValueToUpperCase

        )))
        .forEach((keyLength, map) -> {
            // ...
        });

I like that by writing

                collectingAndThen(
                    convertPairListToMap,
                    convertValueToUpperCase

it is made explicit that the list of Pairs is converted to a Map and that the Values are modified. I don't want to do the uppercase conversion inside the Collector which converts the List to a Map because then this Collector would be doing more than just converting the List of Pairs to a Map as indicated by the name of the Collector. However, I don't like that in this solution the stream is collected two times - this is inefficient, isn't it? I'm therefore wondering if there is a solution to collect the stream only once, but still stating the converion to Map and the converting of the Values explictely. I searched the API for a method to combine two Collectors.toMap call, but couldn't find anything.

1

There are 1 best solutions below

0
Naman On

If you could simplify and share the problem you are attempting to solve with the implementation shared, you would most likely have a better and easier solution.

For what it seems like, the following should do what you are looking for (barring the merging)

Function<Pair<String, String>, Integer> lengthOfKey
        = p -> p.getKey().length();
Collector<Pair<String, String>, ?,
        Map<String, String>> convertToValueMapWithUpperCase =
        Collectors.toMap(Pair<String, String>::getValue,
                e -> e.getValue().toUpperCase());

Map<Integer, Map<String, String>> groupingAndTransformation = list.stream()
        .collect(Collectors.groupingBy(lengthOfKey,
                convertToValueMapWithUpperCase));

The result of the following when executed for the sample

List<Pair<String, String>> list = List.of(
        new Pair("naman", "stackoverflow"),
        new Pair("daniel", "dubov"),
        new Pair("vishy", "anand"),
        new Pair("user", "sample"),
        new Pair("comments", "section"));

is the same as the code shared by you(when printed):

{4={sample=SAMPLE}, 5={anand=ANAND, stackoverflow=STACKOVERFLOW}, 6={dubov=DUBOV}, 8={section=SECTION}}