简体   繁体   中英

Java 8 stream - Merge maps and calculate average of “values”

Let's say I have a List of classes that each has a Map .

public class Test {
    public Map<Long, Integer> map;
}

the Long keys in the Map are timestamps and the Integer values are scores.

I am trying to create a Stream that can combine the maps from all objects and output a Map with the unique Timestamps (The Long s) and an average score.

I have this code, but it gives me the sum of all the scores and not the average (The Integer class doesn't have an average method).

Test test1 = new Test();
    test1.map = new HashMap() {{
        put(1000L, 1);
        put(2000L, 2);
        put(3000L, 3);
    }};

    Test test2 = new Test();
    test2.map = new HashMap() {{
        put(1000L, 10);
        put(2000L, 20);
        put(3000L, 30);
    }};

    List<Test> tests = new ArrayList() {{
        add(test1);
        add(test2);
    }};

    Map<Long, Integer> merged = tests.stream()
            .map(test -> test.map)
            .map(Map::entrySet)
            .flatMap(Collection::stream)
            .collect(
                    Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            Integer::sum

                    )
            );
    System.out.println(merged);

I'm thinking that this might not be an easy problem so solve in a single Stream , so an output with a Map with the unique timestamps and a List of all the scores would also be fine. Then I can calculate the average myself.

Map<Long, List<Integer>> 

It it possible?

Instead of Collectors.toMap use Collectors.groupingBy :

Map<Long, Double> merged = tests.stream()
        .map(test -> test.map)
        .map(Map::entrySet)
        .flatMap(Collection::stream)
        .collect(
                Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collectors.averagingInt(Map.Entry::getValue)
                )
        );

Oh, and even though you probably don't need it anymore, you can get the Map<Long, List<Integer>> you asked about in the last part of your question just as easily:

Map<Long, List<Integer>> merged = tests.stream()
    .map(test -> test.map)
    .map(Map::entrySet)
    .flatMap(Collection::stream)
    .collect(
            Collectors.groupingBy(
                    Map.Entry::getKey,
                    Collectors.mapping(Map.Entry::getValue, Collectors.toList())
            )
    );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM