通过对值进行合并,将地图列表合并到单个地图中

stk*_*305 12 java java-8 java-stream

我有一个类值:

public class Values {
    private int count;
    private int values;
}
Run Code Online (Sandbox Code Playgroud)

以及多个类型的地图列表 Map<String, Values>

Map<String, Values> map1 = new HashMap<>();
        map1 .put("aaa", new Values(1, 10));
        map1 .put("bbb", new Values(5, 50));
        map1 .put("ccc", new Values(2, 30));

Map<String, Values> map2= new HashMap<>();
        map2.put("aaa", new Values(2, 20));
        map2.put("bbb", new Values(3, 50));
        map2.put("ccc", new Values(3, 10));

List<Map<String, Values>> list = Arrays.asList(map1, map2);
Run Code Online (Sandbox Code Playgroud)

地图中可以有任意数量的地图和任意数量的条目,但地图的键始终相同,只有值可以不同.为清晰起见,我的示例仅包含2个地图,每个地图仅包含3个条目.

我想获得一个具有相同键的单个映射,并使用Values对象作为对象中每个"count"和每个"value"的总和,如下所示:

{
        aaa= {
            count = 3,
            value = 30
        },
        bbb= {
            count = 8,
            value = 100
        },
        ccc= {
            count = 6,
            value = 40
        }
    }
Run Code Online (Sandbox Code Playgroud)

我试图使用Streams API实现这一点,但我陷入困境:

public static Map<String, Values> mergeMaps(List<Map<String, Values>> maps) {
        return maps.stream()
                .flatMap(m -> m.entrySet().stream()) ...?
    }
Run Code Online (Sandbox Code Playgroud)

如何通过键对每个条目进行分组,并将每个计数和每个值加到一个映射中?

先感谢您.

Era*_*ran 11

你可以收集你的项目Stream有一个toMap收藏家,有合并功能.

public static Map<String, Values> mergeMaps(List<Map<String, Values>> maps) {
    return maps.stream()
               .flatMap(m -> m.entrySet().stream())
               .collect(Collectors.toMap(Map.Entry::getKey,
                                         Map.Entry::getValue,
                                         (v1,v2) -> new Values(v1,v2)));
}
Run Code Online (Sandbox Code Playgroud)

假设您有一个Values构造函数,它接受两个Values实例并创建一个具有值的总和的实例.

当然,您可以在没有该构造函数的情况下编写合并函数.例如:

(v1,v2) -> new Values(v1.getCount()+v2.getCount(),v1.getValue()+v2.getValue())
Run Code Online (Sandbox Code Playgroud)

  • 关于两个`Values`表示`(v1,v2) - > new Values(v1,v2)`的构造函数,我怀疑有这样一个构造函数的选择有多好.如果确实存在,那么调用`Values :: new`会更干净.在我看来,使用静态方法添加这些属性并返回"Value"应该是更好的选择. (2认同)

Rus*_*lan 7

使用groupingBy的另一个解决方案:

Map<String, Optional<Values>> collect = list.stream()
            .flatMap(map -> map.entrySet().stream())
            .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue,
                    reducing((v1, v2) -> new Values(v1.count + v2.count, v1.values + v2.values)))));
Run Code Online (Sandbox Code Playgroud)

注意:此地图的值为Optional<Values>.如果您null对其中一个源地图有价值,就像map2.put("ddd", null); 它允许避免NullPointerException和返回一样Optional.empty