【问题标题】:Merge list of maps into a single map by summing values通过求和值将地图列表合并为单个地图
【发布时间】:2019-02-10 09:32:20
【问题描述】:

我有一个班级价值观:

public class Values {
    private int count;
    private int values;
}

以及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);

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

我想获得一个具有相同键的单个映射,并将 Values 对象作为对象中每个“计数”和每个“值”的总和,如下所示:

{
        aaa= {
            count = 3,
            value = 30
        },
        bbb= {
            count = 8,
            value = 100
        },
        ccc= {
            count = 6,
            value = 40
        }
    }

我正在尝试使用 Streams API 来实现这一点,但我被卡住了:

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

如何按每个条目的键对每个条目进行分组并将每个计数和每个值添加到单个映射中?

提前谢谢你。

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    您可以使用带有合并功能的toMap 收集器收集Stream 的条目。

    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)));
    }
    

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

    当然,您可以编写没有构造函数的合并函数。例如:

    (v1,v2) -> new Values(v1.getCount()+v2.getCount(),v1.getValue()+v2.getValue())
    

    【讨论】:

    • 关于两个Values 代表(v1,v2) -&gt; new Values(v1,v2) 的构造函数,我怀疑拥有这样一个构造函数的选择有多好。如果这确实存在,那么调用Values::new 会更干净。虽然使用静态方法添加这些属性并返回 Value 在我看来应该是更好的选择。
    【解决方案2】:

    使用 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)))));
    

    注意:此映射的值为Optional&lt;Values&gt;。 如果您的源地图之一上有null 值,例如map2.put("ddd", null); 它允许避免NullPointerException 并返回Optional.empty

    【讨论】:

      猜你喜欢
      • 2017-12-23
      • 1970-01-01
      • 2019-05-02
      • 2023-01-13
      • 2022-01-07
      • 2012-03-21
      • 1970-01-01
      • 2020-10-19
      • 1970-01-01
      相关资源
      最近更新 更多