【问题标题】:How to merge Stream<Map<String, Map<String, String>>> into a single Map<String, Map<String, String>>?如何将 Stream<Map<String, Map<String, String>>> 合并为单个 Map<String, Map<String, String>>?
【发布时间】:2019-06-08 11:27:21
【问题描述】:

我正在尝试将一个 Stream&lt;Map&lt;String, Map&lt;String, String&gt;&gt;&gt; 对象合并到一个带有所有 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() 或任何其他替代方式做到这一点?

【问题讨论】:

  • 请显示getInfoStream的签名和someObject的样子
  • 由于某种原因,问题的原因似乎是getInfoStream 的实现,它返回了Stream&lt;Map&lt;&gt;&gt;,甚至没有Stream&lt;Map.Entry&gt;s。

标签: java java-stream


【解决方案1】:

当你有

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

(我假设是.flatMap(this::getInfoStream)的结果)你可以调用

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

从所有映射创建条目流,这将产生Stream&lt;Map.Entry&lt;String, Map&lt;String, String&gt;&gt;&gt;

现在,您需要从该流中收集每个条目中的键和值到映射中。假设每个键在您可以使用的所有地图中都是唯一的

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

但是如果键不是唯一的,您需要决定应该在新映射中为同一个键放置什么值。我们可以通过填写...部分来做到这一点

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

其中vOld 保存当前保存在同一键下结果映射中的值,vNew 保存新值(来自当前流“迭代”)。
例如,如果您想忽略新值,您可以简单地返回 (vOld, vNew) -&gt; vOld 持有的旧/当前值

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

Map<String, Map<String, String>> combinedMap = 
        /*your Stream<Map<String, Map<String, String>>>*/
        .flatMap(map -> map.entrySet().stream())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

【讨论】:

  • 在我的情况下,地图键是唯一的并且更新了问题。
【解决方案2】:

TL;DR:

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

您可以使用reduceMap&lt;String, Map&lt;String, String&gt;&gt; 元素流合并为一个:

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      在我看来,最易读的方法是将所有内容映射到 Map.Entry,然后使用 Collectors::toMap 将所有内容收集回 Map

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

      (one, two) -&gt; one是合并功能,基本上有重复就随便拿第一个上来

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-21
        • 2013-12-18
        • 2013-05-24
        • 2014-01-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多