java - 使用 Java 8 Stream API 合并 map 列表

标签 java lambda java-8

我正在尝试学习 Java 8 Stream,当我尝试将某些函数转换为 java8 进行练习时。我遇到了一个问题。

我很好奇如何将后续代码转换为 java 流格式。

/*
 * input example:
 * [
    {
        "k1": { "kk1": 1, "kk2": 2},
        "k2": {"kk1": 3, "kk2": 4}
    }
    {
        "k1": { "kk1": 10, "kk2": 20},
        "k2": {"kk1": 30, "kk2": 40}
    }
  ]
 * output:
 * {
        "k1": { "kk1": 11, "kk2": 22},
        "k2": {"kk1": 33, "kk2": 44}
   }
 *
 *
 */
private static Map<String, Map<String, Long>> mergeMapsValue(List<Map<String, Map<String, Long>>> valueList) {
    Set<String> keys_1 = valueList.get(0).keySet();
    Set<String> keys_2 = valueList.get(0).entrySet().iterator().next().getValue().keySet();
    Map<String, Map<String, Long>> result = new HashMap<>();
    for (String k1: keys_1) {
        result.put(k1, new HashMap<>());
        for (String k2: keys_2) {
            long total = 0;
            for (Map<String, Map<String, Long>> mmap: valueList) {
                Map<String, Long> m = mmap.get(k1);
                if (m != null && m.get(k2) != null) {
                    total += m.get(k2);
                }
            }
            result.get(k1).put(k2, total);
        }
    }
    return result;
}

最佳答案

这里的技巧是正确收集内部 map 。工作流程将是:

  • 平面 map map 列表List<Map<String, Map<String, Long>>>进入 map 条目流 Stream<Map.Entry<String, Map<String, Long>>> .
  • 按每个条目的键分组,对于映射到相同键的值,将两个映射合并在一起。

理想情况下,通过合并 map 来收集 map 会保证 flatMapping收集器,不幸的是在 Java 8 中不存在,尽管 it will exist in Java 9 (参见 JDK-8071600)。对于 Java 8,可以使用 StreamEx 提供的那个库(并在以下代码中使用 MoreCollectors.flatMapping )。

private static Map<String, Map<String, Long>> mergeMapsValue(List<Map<String, Map<String, Long>>> valueList) {
    return valueList.stream()
                    .flatMap(e -> e.entrySet().stream())
                    .collect(Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collectors.flatMapping(
                            e -> e.getValue().entrySet().stream(),
                            Collectors.<Map.Entry<String,Long>,String,Long>toMap(Map.Entry::getKey, Map.Entry::getValue, Long::sum)
                        )
                    ));
}

在不使用这个方便的收集器的情况下,我们仍然可以构建自己的具有等效语义的收集器:

private static Map<String, Map<String, Long>> mergeMapsValue2(List<Map<String, Map<String, Long>>> valueList) {
    return valueList.stream()
                    .flatMap(e -> e.entrySet().stream())
                    .collect(Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collector.of(
                            HashMap::new,
                            (r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
                            (r1, r2) -> { r2.forEach((k, v) -> r1.merge(k, v, Long::sum)); return r1; }
                        )
                    ));
}

关于java - 使用 Java 8 Stream API 合并 map 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38072040/

相关文章:

java - GWT、PhoneGap 和视频 VLC

c# - 是否可以将 OrderBy 表达式作为参数传递?

c# - 创建所有 ExpressionTree/Func 参数的数组

java - 如何按 Java 列表中对象的属性进行分组?

java - 使用 Java 8 按属性对自定义对象列表进行分组

java - 大整数、平方根、Java 和 C : What does this line do?

java - 后台服务在 onCreate() 和 onResume() 中随机停止在 android 中工作

java - 词法分析只给出一个输出?

Python 的 lambda 迭代没有按预期工作

java - 从内部类到外部接口(interface)的非静态访问的基本障碍