Merging two Map with Java 8 Stream API

前端 未结 6 1126
孤城傲影
孤城傲影 2020-11-30 19:17

I have two (or more) Map objects. I\'d like to merge them with Java 8 Stream API in a way that values for common keys should be the maxim

相关标签:
6条回答
  • 2020-11-30 19:32

    I added my contribution to the proton pack library which contains utility methods for the Stream API. Here's how you could achieve what you want:

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

    Basically mergeKeys will collect the key-value pairs in a new map (providing a merge function is optional, you'll end up with a Map<String, List<Integer>> otherwise) and recall stream() on the entrySet() to get a new MapStream. Then use collect() to get the resulting map.

    0 讨论(0)
  • 2020-11-30 19:36
    @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);
    }
    
    0 讨论(0)
  • 2020-11-30 19:36
    Map<String, Integer> mx = new HashMap<>(m1);
    m2.forEach((k, v) -> mx.merge(k, v, Integer::max));
    
    0 讨论(0)
  • 2020-11-30 19:47

    I've created a visual representation of what @srborlongan did, for anyone who might be interested.

    0 讨论(0)
  • 2020-11-30 19:51
    mx = list.stream().collect(HashMap::new,
            (a, b) -> b.forEach((k, v) -> a.merge(k, v, Integer::max)),
            Map::putAll);
    

    This covers the general case for any size list and should work with any types, just swap out the Integer::max and/or HashMap::new as desired.

    If you don't care which value comes out in a merge, there's a much cleaner solution:

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

    And as generic methods:

    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);
    }
    
    0 讨论(0)
  • 2020-11-30 19:52

    Using StreamEx you can do:

    StreamEx.of(m1, m2)
        .flatMapToEntry(x -> x)
        .grouping(IntCollector.max())
    
    0 讨论(0)
提交回复
热议问题