Search code examples
javajava-streammultimap

How to combine Map values from parent Map with java 8 stream


I have a map inside a map which looks like this:

Map<String, Map<Integer, BigDecimal>> mapInMap; //--> with values like: 
/*
"a": (1: BigDecimal.ONE),
"b": (2: BigDecimal.TEN),
"c": (1: BigDecimal.ZERO)
*/

And I would like to combine the inner maps by expecting the following result:

Map<Integer, BigDecimal> innerMapCombined; //--> with values:
/*
1: BigDecimal.ZERO,
2: BigDecimal.TEN
*/

This is my solution with predefining the combined map and using forEach:

Map<Integer, BigDecimal> combined = new HashMap<>();
mapInMap.forEach((str, innerMap) -> {
  innerMap.forEach(combined::putIfAbsent);
});

But this will ignore (1: BigDecimal.ZERO).

Could you provide a 1-line solution with java 8 stream?


Solution

  • The issue with your problem is that as soon as you initialize your maps, and add the duplicate keys on the inner maps, you will rewrite those keys, since those maps do not accept duplicated keys. Therefore, you need to first change this:

    Map<String, Map<Integer, BigDecimal>> mapInMap;
    

    to a Map that allows duplicated keys, for instance Multimap from Google Guava:

    Map<String, Multimap<Integer, BigDecimal>> mapInMap = new HashMap<>();
    

    where the inner maps are created like this:

     Multimap<Integer, BigDecimal> x1 = ArrayListMultimap.create();
     x1.put(1, BigDecimal.ONE);
     mapInMap.put("a", x1);
    

    Only now you can try to solve your problem using Java 8 Streams API. For instance:

    Map<Integer, BigDecimal> map = multiMap.values()
                                           .stream()
                                           .flatMap(map -> map.entries().stream())
                                           .collect(Collectors.toMap(Map.Entry::getKey,
                                                                     Map.Entry::getValue, 
                                                                     (v1, v2) -> v2));
    

    The duplicate keys conflicts are solved using mergeFunction parameter of the toMap method. We explicitly express to take the second value (v1, v2) -> v2 in case of duplicates.