2014-04-13 24 views
46

Tôi có hai (hoặc nhiều hơn) Map<String, Integer> đối tượng. Tôi muốn hợp nhất chúng với API Java 8 Stream theo cách mà các giá trị cho các khóa chung nên là giá trị tối đa của các giá trị.Hợp nhất hai Bản đồ <Chuỗi, Số nguyên> với API 8 API Java

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

Làm cách nào để làm phương pháp thử nghiệm này có màu xanh lục?

Tôi đã chơi với collectCollectors trong một thời gian mà không thành công.

(ImmutableMapnewArrayList là từ Google ổi.)

Trả lời

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

Tuyệt vời! Tôi chỉ cần một thứ khác, thay vì tối đa, tôi cần trung bình. Tôi làm nó như thế nào? –

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

này bao gồm các trường hợp chung cho bất kỳ danh sách kích thước và sẽ làm việc với bất kỳ loại, chỉ trao đổi trên các Integer::max và/hoặc HashMap::new như mong muốn.

Nếu bạn không quan tâm mà giá trị đi ra trong một hợp nhất, có một giải pháp sạch hơn nhiều:

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

Và như các phương pháp chung:

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

tôi thêm sự đóng góp của tôi vào proton pack library chứa các phương pháp tiện ích cho API luồng. Đây là cách bạn có thể đạt được những gì bạn muốn:

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

Về cơ bản mergeKeys sẽ thu thập các cặp khóa-giá trị trong một bản đồ mới (cung cấp một chức năng hợp nhất là không bắt buộc, bạn sẽ kết thúc với một Map<String, List<Integer>> khác) và nhớ lại stream() trên số entrySet() để nhận số MapStream mới. Sau đó sử dụng collect() để có được bản đồ kết quả.

1

Sử dụng StreamEx bạn có thể làm:

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

Đây là qua kỹ thuật, bạn có thể làm chỉ là:

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

Điều này không đảm bảo điều kiện này: "giá trị cho các phím chung phải là tối đa các giá trị ". – palacsint

+1

Nó thậm chí không cố gắng để _address_ điều kiện đó. –

1

tôi đã tạo ra một hình ảnh của những gì @srborlongan đã làm, cho bất cứ ai có thể quan tâm.

enter image description here

Các vấn đề liên quan