2014-04-13 34 views
46

İki (veya daha fazla) Map<String, Integer> nesnem var. Java 8 Stream API'sını ortak anahtarlara yönelik değerlerin en fazla değer olması gerektiği şekilde birleştirmek istiyorum.İki Eşleme <String, Tamsayı> Java 8 Akış API'siyle Birleştirme

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

Bu test yöntemini nasıl yeşil yapabilirim?

collect ve Collectors ile bir süreliğine başarıya ulaşmadan oynadım.

(ImmutableMap ve newArrayList tarihinde Guava vardır.)

cevap

68
@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

Great! Sadece bir şeylere ihtiyacım var, max yerine, ortalamaya ihtiyacım var. Nasıl yapabilirim? –

+1

@FirasAlMannaa https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#average-- –

48
Map<String, Integer> mx = new HashMap<>(m1); 
m2.forEach((k, v) -> mx.merge(k, v, Integer::max)); 
12
mx = list.stream().collect(HashMap::new, 
     (a, b) -> b.forEach((k, v) -> a.merge(k, v, Integer::max)), 
     Map::putAll); 

Bu, herhangi bir boyut listesi için genel durum kapak ve herhangi türleri ile çalışması gerekir, sadece Integer::max aktarır ve/veya arzu edildiği gibi HashMap::new.

orada bir daha temiz bir çözüm, bir birleştirme çıkar hangi değerin umursamıyorsan:

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

Ve jenerik yöntemler olarak:

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

Ben proton pack library payım eklendi Stream API için yardımcı yöntemler içerir.

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

yeni haritası anahtar-değer çiftleri toplayacak (bir birleştirme işlevi isteğe bağlıdır sağlayarak, yoksa bir Map<String, List<Integer>> ile bitireceğiz) ve stream() hatırlama: Burada ne istediğinizi elde edebiliriz nasıl Yeni bir MapStream almak için entrySet() üzerinde. Ardından ortaya çıkan haritayı elde etmek için collect() kullanın.

1

StreamEx kullanarak yapabilirsiniz: Bu mühendislik bitti

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

, sadece yapabilirsiniz:

map3 = new HashMap<>(); 
map3.putAll(map1); 
map3.putAll(map2); 
+5

Bu durum aşağıdakileri garanti etmez: "ortak anahtarlar için bu değerler maksimum değer ". – palacsint

+1

Bu durumu _address_ denemeye bile kalkışmaz. –

1

hiç kimse için, @srborlongan yaptıklarını görsel bir temsilini oluşturduk kim ilgilenebilir.

enter image description here