有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用Java8流API合并两个Map<String,Integer>

我有两个(或更多)Map<String, Integer>对象。我希望将它们与Java8流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来自谷歌番石榴。)


共 (6) 个答案

  1. # 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. # 2 楼答案

    我在proton pack library中添加了我的贡献,其中包含流API的实用方法。以下是你如何实现自己的目标:

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

    基本上mergeKeys将在一个新映射中收集键值对(提供一个可选的合并函数,否则最终将得到一个Map<String, List<Integer>>),并在entrySet()上调用stream()以获得一个新的MapStream。然后使用collect()获得结果图

  3. # 3 楼答案

    我已经为可能感兴趣的任何人创建了@srborlongan所做工作的视觉表现

    Diagram displaying maps convert to stream of entries

  4. # 4 楼答案

    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);
    }
    
  5. # 5 楼答案

    Map<String, Integer> mx = new HashMap<>(m1);
    m2.forEach((k, v) -> mx.merge(k, v, Integer::max));
    
  6. # 6 楼答案

    使用StreamEx可以执行以下操作:

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