>" to "Map>"? Map> collectionsMap = " /> >" to "Map>"? Map> collectionsMap = " /> >" to "Map>"? Map> collectionsMap = "/>

From "Map<String, Collection<String>>" to "Map<String, List<String>>" using Java 8

3.3k Views Asked by At

Is there a better way to transform "Map<String, Collection<String>>" to "Map<String, List<String>>"?

Map<String, Collection<String>> collectionsMap = ...
Map<String, List<String>> listsaps =
    collectionsMap.entrySet().stream()
    .collect(Collectors.<Map.Entry<String, Collection<String>>,
        String, List<String>>toMap(
            Map.Entry::getKey,
            e -> e. getValue().stream().collect(Collectors.toList())
        )
    );

Thank you for helping us improve

3

There are 3 best solutions below

2
Michael On

I think that this is easier to read:

Map<String, List<String>> listsaps = new HashMap<>();
collectionsMap.entrySet()
    .stream()
    .forEach(e -> listsaps.put(e.getKey(), new ArrayList<>(e.getValue())));

If you just want to convert the entries to lists but don't really care about changing the type of the collection then you can use map.replaceAll:

collectionsMap.replaceAll((k, v) -> new ArrayList<>(v));
0
davidxxx On

1) In Collectors.toMap() you don't need to repeat the generic types as these are inferred.

So :

collect(Collectors.<Map.Entry<String, Collection<String>>,
        String, List<String>>toMap(...)

can be replaced by :

collect(Collectors.toMap(...)

2) The way of transforming the collection into a List could also be simplified.

This :

e -> e. getValue().stream().collect(Collectors.toList())

could be written as :

e -> new ArrayList<>(e.getValue())

You could write :

Map<String, List<String>> listsaps =
            collectionsMap.entrySet()
            .stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    e -> new ArrayList<>(e.getValue())
                )
            );
6
Stuart Marks On

For cases like this, I'd consider using Map.forEach to perform the operation using side effects. Streams over maps are somewhat cumbersome, as one needs to write extra code to stream the map entries and then extract the key and value from each entry. By contrast, Map.forEach passes each key and value to the function as a separate parameter. Here's what that looks like:

Map<String, Collection<String>> collectionsMap = ...
Map<String, List<String>> listsaps = new HashMap<>(); // pre-size if desired
collectionsMap.forEach((k, v) -> listsaps.put(k, new ArrayList<>(v)));

If your map is large, you'll probably want to pre-size the destination in order to avoid rehashing during its population. To do this properly you have to know that HashMap takes the number of buckets, not the number of elements, as its parameter. This requires dividing by the default load factor of 0.75 in order to pre-size properly given a certain number of elements:

Map<String, List<String>> listsaps = new HashMap<>((int)(collectionsMap.size() / 0.75 + 1));