Flatten a Map> to Map with stream and lambda

后端 未结 4 571
不知归路
不知归路 2020-12-13 02:37

I would like to flatten a Map which associates an Integer key to a list of String, without losing the key mapping. I am curious as tho

相关标签:
4条回答
  • 2020-12-13 03:12

    This should work. Please notice that you lost some keys from List.

    Map<Integer, List<String>> mapFrom = new HashMap<>();
    Map<String, Integer> mapTo = mapFrom.entrySet().stream()
            .flatMap(integerListEntry -> integerListEntry.getValue()
                    .stream()
                    .map(listItem -> new AbstractMap.SimpleEntry<>(listItem, integerListEntry.getKey())))
            .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
    
    0 讨论(0)
  • 2020-12-13 03:21

    Hope this would do it in simplest way. :))

    mapFrom.forEach((key, values) -> values.forEach(value -> mapTo.put(value, key)));
    
    0 讨论(0)
  • 2020-12-13 03:23

    You should use flatMap as follows:

    entrySet.stream()
            .flatMap(e -> e.getValue().stream()
                           .map(s -> new SimpleImmutableEntry(e.getKey(), s)));
    

    SimpleImmutableEntry is a nested class in AbstractMap.

    0 讨论(0)
  • 2020-12-13 03:31

    You need to use flatMap to flatten the values into a new stream, but since you still need the original keys for collecting into a Map, you have to map to a temporary object holding key and value, e.g.

    Map<String, Integer> mapTo = mapFrom.entrySet().stream()
           .flatMap(e->e.getValue().stream()
                        .map(v->new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v)))
           .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
    

    The Map.Entry is a stand-in for the nonexistent tuple type, any other type capable of holding two objects of different type is sufficient.

    An alternative not requiring these temporary objects, is a custom collector:

    Map<String, Integer> mapTo = mapFrom.entrySet().stream().collect(
        HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v, e.getKey())), Map::putAll);
    

    This differs from toMap in overwriting duplicate keys silently, whereas toMap without a merger function will throw an exception, if there is a duplicate key. Basically, this custom collector is a parallel capable variant of

    Map<String, Integer> mapTo = new HashMap<>();
    mapFrom.forEach((k, l) -> l.forEach(v -> mapTo.put(v, k)));
    

    But note that this task wouldn’t benefit from parallel processing, even with a very large input map. Only if there were additional computational intense task within the stream pipeline that could benefit from SMP, there was a chance of getting a benefit from parallel streams. So perhaps, the concise, sequential Collection API solution is preferable.

    0 讨论(0)
提交回复
热议问题