【问题标题】:java8 merge two map which structure is Map<String, Map<String, Set<Long>>> in concisejava8合并两个map,其结构是Map<String, Map<String, Set<Long>>>
【发布时间】:2017-05-19 22:53:09
【问题描述】:

使用java8,有没有什么简洁的语法可以合并两个map:

    Map<String, Map<String, Set<Long>>> m1

    Map<String, Map<String, Set<Long>>> m2

并且不要改变m1、m2中的任何元素,甚至改变mergedMap。

例如:

m1 包含 2 个类似的元素:

{
    "k1": {
        "v1": [
            11, 
            12
        ]
    }, 
    "k2": {
        "v2": [
            21
        ]
    }
}

m2 包含 3 个元素,如下所示:

{
    "k1": {
        "v11": [
            11
        ]
    }, 
    "k2": {
        "v2": [
            21, 
            22
        ]
    }, 
    "k3": {
        "v3": [
            31
        ]
    }
}

我想要的合并地图是合并的3个元素。

尤其是“k1”的值结合了 m1 和 m2。

{
    "k1": {
        "v1": [
            11, 
            12
        ], 
        "v11": [
            11
        ]
    }, 
    "k2": {
        "v2": [
            21, 
            22
        ]
    }, 
    "k3": {
        "v3": [
            31
        ]
    }
}

当我在合并的 Map 中添加一些元素时,哪个键是 >。

我不想再修改原始地图 m2 的元素了。

【问题讨论】:

  • 嗯,流可能会有所帮助 - 但目前尚不清楚,您到底想要什么结果?
  • 我在我的问题中添加了一个例子,感谢您的回复!

标签: merge hashmap java-8 hashset


【解决方案1】:

在不修改源集合的情况下合并嵌套集合比乍一看要复杂得多。当执行就地合并、修改其中一个源映射时,Map.merge 的嵌套应用程序就可以了,但是为了将嵌套数据收集到新容器中,需要类似于 Java 9 的构造 flatMapping collector。使用此收集器,解决方案可能如下所示:

Map<String, Map<String, Set<Long>>> m3 =
  Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
    .collect(groupingBy(Map.Entry::getKey,
      flatMapping(e -> e.getValue().entrySet().stream(),
        groupingBy(Map.Entry::getKey, flatMapping(e -> e.getValue().stream(), toSet())))));

当使用 flatMapping 收集器的反向端口时,这也适用于 Java 8(参见 this answer 的末尾)。

考虑到要合并的Maps 的数量相当少,另一种方法是创建专门用于此用例的收集器:

static <T> Collector<Set<T>,?,Set<T>> mergeSets() {
    return Collector.of(HashSet::new, Set::addAll, (s1,s2)->{ s1.addAll(s2); return s1; });
}
static <K,V> Collector<Map<K,V>,?,Map<K,V>> mergeMaps(Collector<V,?,V> c) {
    return collectingAndThen(reducing(
        (m1,m2) -> Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
            .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, c)))),
        o -> o.orElse(Collections.emptyMap()));
}

这些收集器可以组合起来完成这项工作:

Map<String, Map<String, Set<Long>>> m3 =
    Stream.of(m1, m2).collect(mergeMaps(mergeMaps(mergeSets())));

【讨论】:

  • 很棒的答案,但你的最后一个解决方案太恶心了。如果可以的话 +2。
【解决方案2】:

种类:

HashMap<String, Map<String, Set<String>>> m1 = new HashMap<>();
HashMap<String, Map<String, Set<String>>> m2 = new HashMap<>();

Stream.of(m1,m2).flatMap( m -> m.entrySet().stream())
        // stream of entries <String,Set<String>
        .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (s1, s2) -> { /* do whatever  you like to merge your nested  maps */ return new HashMap<>()}  )

`

当然,您必须合并嵌套地图。 这将使原始数据结构保持原样

【讨论】:

    【解决方案3】:

    Streams 并不是要取代一切,所以这是我的观点:

    for (Map.Entry<String, Map<String, Set<Long>>> entry : m2.entrySet()) {
            m1.merge(entry.getKey(), entry.getValue(), (v1, v2) -> {
                for (Map.Entry<String, Set<Long>> e : v2.entrySet()) {
                    v1.merge(e.getKey(), e.getValue(), (s1, s2) -> {
                        s1.addAll(s2);
                        return s1;
                    });
                }
                return v1;
            });
        }
    

    或者在其他建议的答案中使用 flatMap

              Stream.of(m1, m2)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> {
                    for (Map.Entry<String, Set<Long>> e : v2.entrySet()) {
                        v1.merge(e.getKey(), e.getValue(), (s1, s2) -> {
                            s1.addAll(s2);
                            return s1;
                        });
                    }
                    return v1;
                }, HashMap::new));
    

    有你的意见:

    [k1={v1=[11, 12]}, k2={v2=[21]}]
    {k1={v11=[11]}, k2={v2=[21, 22]}, k3={v3=[31]}}
    

    输出将是:

       {k1={v11=[11], v1=[11, 12]}, k2={v2=[21, 22]}, k3={v3=[31]}}
    

    【讨论】:

    • 嗯,这确实修改了源数据……
    • @Holger 没有注意到关于不修改输入的部分。我的坏
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多