将两个Map <string,integer =“”>与Java 8 Stream API合并</string,>

时间:2014-04-13 03:09:03

标签: java merge java-8 java-stream

我有两个(或更多)Map<String, Integer>个对象。我希望将它们与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。)

6 个答案:

答案 0 :(得分:110)

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

答案 1 :(得分:63)

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

答案 2 :(得分:15)

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

答案 3 :(得分:11)

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

Diagram displaying maps convert to stream of entries

答案 4 :(得分:2)

我将我的贡献添加到proton pack library,其中包含Stream API的实用程序方法。以下是您如何实现自己想要的目标:

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

基本上mergeKeys将在新地图中收集键值对(提供合并功能是可选的,否则最终会以Map<String, List<Integer>>结束)并回忆{{1}在stream()上获取新的entrySet()。然后使用MapStream获取生成的地图。

答案 5 :(得分:1)

使用StreamEx即可:

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