Iterate though a Map of Maps with Lambda in Java 8

2019-03-19 11:49发布

问题:

I am new to java and lambda, I want to find the sum and average of values in a map of maps.

My object is like Map<String, Map<String, Double>> browserData;

Data is of the format

<Chrome, <UK, 2.90>>
<Chrome, <US, 5.20>>
<Chrome, <EU, -0.25>>
<IE, <UK, 0.1>>
<IE, <US, -.20>>
<IE, <EU, 0.00>>
<FF, <UK, 0.90>>
<FF, <US, 1.20>>
<FF, <EU, 1.25>>

The final result needs to be two maps, 1 for sum and another for average

map1 = <CountryName, SumValue>

map2 = <CountryName, AverageValue>

So the result of the above example it would be

map1 = <UK, 3.9>
       <US, 6.2>
       <EU, 1>

map2 = <UK, 1.3>
       <US, 2.07>
       <EU, 0.33>

How can I achieve this?

回答1:

The idea is to stream each of the entries of the inner maps and apply the appropriate collector to calculate the values you need:

Map<String, DoubleSummaryStatistics> stats = browserData.values().stream()
        .flatMap(m -> m.entrySet().stream()) //Stream the inner maps' entrySets
        .collect(groupingBy(Entry::getKey, summarizingDouble(Entry::getValue)));
DoubleSummaryStatistics ds = stats.getOrDefault("EU", new DoubleSummaryStatistics());
System.out.println("sumEu = " + ds.getSum());
System.out.println("avgEu = " + ds.getAverage());

If you do need the individual sum and average maps, you can create them from the summary map:

Map<String, Double> map1 = stats.entrySet().stream()
        .collect(toMap(Entry::getKey, e -> e.getValue().getSum()));
Map<String, Double> map2 = stats.entrySet().stream()
        .collect(toMap(Entry::getKey, e -> e.getValue().getAverage()));

note: I have used the following static imports:
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summarizingDouble;
import static java.util.stream.Collectors.toMap;