【问题标题】:Merging two Map<String, Integer> with Java 8 Stream API使用 Java 8 Stream API 合并两个 Map<String, Integer>
【发布时间】:2014-05-27 03:34:00
【问题描述】:

我有两个(或更多)Map&lt;String, Integer&gt; 对象。我想将它们与 Java 8 Stream API 合并,使公共键的值应该是值的最大值。

@Test
public void test14() throws Exception {
    Map<String, Integer> m1 = ImmutableMap.of("a", 2, "b", 3);
    Map<String, Integer> m2 = ImmutableMap.of("a", 3, "c", 4);
    List<Map<String, Integer>> list = newArrayList(m1, m2);

    Map<String, Integer> mx = list.stream()... // TODO

    Map<String, Integer> expected = ImmutableMap.of("a", 3, "b", 3, "c", 4);
    assertEquals(expected, mx);
}

我怎样才能使这个测试方法变成绿色?

我和collectCollectors 玩了一段时间没有任何成功。

ImmutableMapnewArrayList 来自 Google Guava。)

【问题讨论】:

    标签: java merge java-8 java-stream


    【解决方案1】:
    @Test
    public void test14() throws Exception {
        Map<String, Integer> m1 = ImmutableMap.of("a", 2, "b", 3);
        Map<String, Integer> m2 = ImmutableMap.of("a", 3, "c", 4);
    
        Map<String, Integer> mx = Stream.of(m1, m2)
            .map(Map::entrySet)          // converts each map into an entry set
            .flatMap(Collection::stream) // converts each set into an entry stream, then
                                         // "concatenates" it in place of the original set
            .collect(
                Collectors.toMap(        // collects into a map
                    Map.Entry::getKey,   // where each entry is based
                    Map.Entry::getValue, // on the entries in the stream
                    Integer::max         // such that if a value already exist for
                                         // a given key, the max of the old
                                         // and new value is taken
                )
            )
        ;
    
        /* Use the following if you want to create the map with parallel streams
        Map<String, Integer> mx = Stream.of(m1, m2)
            .parallel()
            .map(Map::entrySet)          // converts each map into an entry set
            .flatMap(Collection::stream) // converts each set into an entry stream, then
                                         // "concatenates" it in place of the original set
            .collect(
                Collectors.toConcurrentMap(        // collects into a map
                    Map.Entry::getKey,   // where each entry is based
                    Map.Entry::getValue, // on the entries in the stream
                    Integer::max         // such that if a value already exist for
                                         // a given key, the max of the old
                                         // and new value is taken
                )
            )
        ;
        */
    
        Map<String, Integer> expected = ImmutableMap.of("a", 3, "b", 3, "c", 4);
        assertEquals(expected, mx);
    }
    

    【讨论】:

    【解决方案2】:
    Map<String, Integer> mx = new HashMap<>(m1);
    m2.forEach((k, v) -> mx.merge(k, v, Integer::max));
    

    【讨论】:

      【解决方案3】:
      mx = list.stream().collect(HashMap::new,
              (a, b) -> b.forEach((k, v) -> a.merge(k, v, Integer::max)),
              Map::putAll);
      

      这涵盖了任何大小列表的一般情况,应该适用于任何类型,只需根据需要换出Integer::max 和/或HashMap::new

      如果您不关心合并中出现的值,那么有一个更简洁的解决方案:

      mx = list.stream().collect(HashMap::new, Map::putAll, Map::putAll);
      

      作为通用方法:

      public static <K, V> Map<K, V> mergeMaps(Stream<? extends Map<K, V>> stream) {
          return stream.collect(HashMap::new, Map::putAll, Map::putAll);
      }
      
      public static <K, V, M extends Map<K, V>> M mergeMaps(Stream<? extends Map<K, V>> stream,
              BinaryOperator<V> mergeFunction, Supplier<M> mapSupplier) {
          return stream.collect(mapSupplier,
                  (a, b) -> b.forEach((k, v) -> a.merge(k, v, mergeFunction)),
                  Map::putAll);
      }
      

      【讨论】:

        【解决方案4】:

        我为任何可能感兴趣的人创建了 @srborlongan 所做工作的可视化表示。

        【讨论】:

          【解决方案5】:

          我将我的贡献添加到 proton pack library,其中包含 Stream API 的实用方法。您可以通过以下方式实现您想要的:

          Map<String, Integer> mx = MapStream.ofMaps(m1, m2).mergeKeys(Integer::max).collect();
          

          基本上mergeKeys 将在新映射中收集键值对(如果合并功能是可选的,否则您将得到一个Map&lt;String, List&lt;Integer&gt;&gt;)并在entrySet() 上调用stream() 以获得一个新的MapStream。然后使用collect() 得到结果图。

          【讨论】:

            【解决方案6】:

            使用StreamEx 你可以做到:

            StreamEx.of(m1, m2)
                .flatMapToEntry(x -> x)
                .grouping(IntCollector.max())
            

            【讨论】:

              猜你喜欢
              • 2017-11-22
              • 2015-07-08
              • 1970-01-01
              • 2023-03-26
              • 2017-01-28
              • 2022-07-05
              • 1970-01-01
              • 1970-01-01
              • 2017-02-12
              相关资源
              最近更新 更多