如何将Stream <Map <String,Map <String,String >>>合并到单个Map <String,Map <String,String >>中?

时间:2019-06-08 11:27:21

标签: java java-stream

我正在尝试将Stream<Map<String, Map<String, String>>>对象合并到一个具有所有Streams中键的单个映射中。

例如,

final Map<String, someOtherObjectToProcess> someObject;

final List<Map<String, Map<String, String>>> list = someObject.entrySet()
            .stream()
            .flatMap(this::getInfoStream)
            .collect(Collectors.toList());

getInfoStream的签名是

public Stream<Map<String, Map<String, String>>> getInfoStream(Map.Entry<String, someOtherObjectToProcess> entry)

如果我使用(Collectors.toList()),则可以获取这些Map对象的列表。

如果使用上述代码,则输出示例:

[{
    "name" : {
        "property":"value"
    }
},

{
    "name2" : {
        "property":"value"
    }
}]

但是我想用结构收集到地图中

{
    "name" : {
        "property":"value"
    },
    "name2" : {
        "property":"value"
    }
}

提供了唯一的键。

我该如何使用Collectors.toMap()或其他任何替代方法?

4 个答案:

答案 0 :(得分:3)

有空

Stream<Map<String, Map<String, String>>> stream = ...

(我假设是.flatMap(this::getInfoStream)的结果),您可以致电

.flatMap(map -> map.entrySet().stream())

从所有地图创建条目流,这些条目将产生Stream<Map.Entry<String, Map<String, String>>>

现在从该流中,您要做的就是从地图的每个条目中 collect 键和值。假设每个键在您可以使用的所有地图上都是唯一的

.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

但是,如果键不是唯一的,则需要确定应在同一键的新映射中放置什么值。我们可以通过填充...

.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (vOld, vNew) -> ...));
//                                                                                ^^^

其中vOld保留当前在相同键下的结果映射中保留的值,而vNew保留新值(来自当前流“ iteration”)。
例如,如果您想忽略新值,则可以简单地返回{/ {1}}

保留的旧值/当前值

简而言之(假设唯一键):

(vOld, vNew) -> vOld

答案 1 :(得分:1)

解决此问题的另一种方法不是使用collector(toList()),而是使用供应商,累加器和合并器的另一种重载.collect()方法:

Stream<Map<String, Map<String, String>>> stream = ...
Map<String, Map<String, String>> result = stream
       .collect(HashMap::new, HashMap::putAll, HashMap::putAll);

答案 2 :(得分:1)

我认为最易读的方式是将所有内容映射到app.use(...) ,然后使用Map.Entry将所有内容收集回Map

Collectors::toMap

import static java.util.stream.Collectors.toMap; // ... someObject.entrySet() .stream() .flatMap(this::getInfoStream) .flatMap(map -> map.entrySet().stream()) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (one, two) -> one)); 是合并功能,基本上,如果您有重复项,则可以任意选择第一个出现的

答案 3 :(得分:1)

TL; DR:

var merged = Stream.of(map1, map2, ..., mapN).reduce(new HashMap<>(), (a, b) -> {
    a.putAll(b);
    return a;
});

您可以使用reduceMap<String, Map<String, String>>元素流合并为一个:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {
        alternative1();
        alternative2();
    }

    // Use reduce without an initial identity value
    public static void alternative1() {
        Map<String, Map<String, String>> m1 = new HashMap<>();
        m1.put("name", Map.of("property", "value"));

        Map<String, Map<String, String>> m2 = new HashMap<>();
        m2.put("name2", Map.of("property", "value"));

        Stream<Map<String, Map<String, String>>> mapStream = Stream.of(m1, m2);

        Map<String, Map<String, String>> m3 = mapStream.reduce((a, b) -> {
            Map<String, Map<String, String>> temp = new HashMap<>();
            temp.putAll(a);
            temp.putAll(b);
            return temp;
        }).orElseThrow();

        System.out.println(m3);
    }

    // Use reduce with an initial empty map as the identity value
    public static void alternative2() {
        Map<String, Map<String, String>> m1 = new HashMap<>();
        m1.put("name", Map.of("property", "value"));

        Map<String, Map<String, String>> m2 = new HashMap<>();
        m2.put("name2", Map.of("property", "value"));

        Stream<Map<String, Map<String, String>>> mapStream = Stream.of(m1, m2);

        Map<String, Map<String, String>> m3 = mapStream.reduce(new HashMap<>(), (a, b) -> {
            a.putAll(b);
            return a;
        });

        System.out.println(m3);
    }
}

输出:

{name={property=value}, name2={property=value}}
{name={property=value}, name2={property=value}}

但是请注意,这些解决方案假定键(namename2)是唯一的,否则重复的键会使映射条目相互覆盖。


具有更现代语法的相同逻辑:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {
        alternative1();
        alternative2();
    }

    // Use reduce without an initial identity value
    public static void alternative1() {
        var m1 = Map.of("name", Map.of("property", "value"));
        var m2 = Map.of("name2", Map.of("property", "value"));
        var m3 = Stream.of(m1, m2).reduce((a, b) -> {
            var temp = new HashMap<String, Map<String, String>>();
            temp.putAll(a);
            temp.putAll(b);
            return temp;
        }).orElseThrow();

        System.out.println(m3);
    }

    // Use reduce with an initial empty map as the identity value
    public static void alternative2() {
        var m1 = Map.of("name", Map.of("property", "value"));
        var m2 = Map.of("name2", Map.of("property", "value"));
        var m3 = Stream.of(m1, m2).reduce(new HashMap<>(), (a, b) -> {
            a.putAll(b);
            return a;
        });

        System.out.println(m3);
    }
}