java8合并两个映射,其结构是Map <string,map <string,=“”set <long =“”>&gt;&gt;简明扼要

时间:2017-01-05 11:47:33

标签: merge hashmap java-8 hashset

使用java8,是否有任何简洁的语法来合并两个地图:

    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个元素。

特别是,&#34; k1&#34;的值由m1和m2组合而成。

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

当我在合并的Map中添加一些元素时,键是&lt;&#34; k3&#34;,&lt;&#34; v3&#34;,???&gt;&gt;。

我不希望原始地图m2的元素再次修改。

3 个答案:

答案 0 :(得分:4)

在不修改源集合的情况下合并嵌套集合比第一眼看上去更复杂。在原地执行合并,修改其中一个源映射时,Map.merge的嵌套应用程序将执行,但是为了将嵌套数据收集到新容器中,类似于Java 9的构造{{3}是必要的。使用此收集器,解决方案可能如下所示:

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(参见flatMapping collector的末尾)。

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

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

答案 1 :(得分: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<>()}  )

`

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

答案 2 :(得分:1)

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]}}