Java 8 Map merge一些騷操作

導語

添加小編微信:372787553,帶您進入java學習交流羣

1. 介紹

本入門教程將介紹Java8中如何合併兩個map。

更具體說來,我們將研究不同的合併方案,包括Map含有重複元素的情況。

2. 初始化

我們定義兩個map實例

private static Map<String, Employee> map1 = new HashMap<>();
private static Map<String, Employee> map2 = new HashMap<>();

Employee類

public class Employee {
    private Long id;
    private String name;
    // 此處省略構造方法, getters, setters方法
}

然後往map中存入一些數據

 Employee employee1 = new Employee(1L, "Henry");
 map1.put(employee1.getName(), employee1);
 Employee employee2 = new Employee(22L, "Annie");
 map1.put(employee2.getName(), employee2);
 Employee employee3 = new Employee(8L, "John");
 map1.put(employee3.getName(), employee3);

 Employee employee4 = new Employee(2L, "George");
 map2.put(employee4.getName(), employee4);
 Employee employee5 = new Employee(3L, "Henry");
 map2.put(employee5.getName(), employee5);

特別需要注意的是employee1employee5在map中有完全相同的key(name)。

3. Map.merge()

Java8爲 java.util.Map接口新增了merge()函數。

merge() 函數的作用是: 如果給定的key之前沒設置value 或者value爲null, 則將給定的value關聯到這個key上.

否則,通過給定的remaping函數計算的結果來替換其value。如果remapping函數的計算結果爲null,將解除此結果。
源碼:

    V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if (newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

First, let’s construct a new HashMap by copying all the entries from the map1:

首先,我們通過拷貝map1中的元素來構造一個新的HashMap

Map<String, Employee> map3 = new HashMap<>(map1);

然後引入merge函數和合並規則

map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())

最後對map2進行迭代將其元素合併到map3中

map2.forEach(
  (key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));

運行程序並打印結果如下:

John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
Henry=Employee{id=1, name='Henry'}

最終,通過結果可以看出,實現了兩個map的合併,對重複的key也合併爲同一個元素。

注意最後一個Employee的id來自map1而name來自map2.

原因是我們的merge函數的定義

(v1, v2) -> new Employee(v1.getId(), v2.getName())

4. Stream.concat()

Java8的Stream API 也爲解決該問題提供了較好的解決方案。

首先需要將兩個map合爲一個Stream。

Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());

我們需要將entry sets作爲參數,然後利用Collectors.toMap():將結果放到新的map中。

Map<String, Employee> result = combined.collect(
  Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

該方法可以實現map的合併,但是有重複key會報IllegalStateException異常。

爲了解決這個問題,我們需要加入lambda表達式merger作爲第三個參數

(value1, value2) -> new Employee(value2.getId(), value1.getName())

當檢測到有重複Key時就會用到該lambda表達式。

現在把上面代碼組合在一起:

Map<String, Employee> result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey, 
    Map.Entry::getValue,
    (value1, value2) -> new Employee(value2.getId(), value1.getName())));

最終的結果

George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=3, name='Henry'}

從結果可以看出重複的key Henry”將合併爲一個新的鍵值對,id取自map2,name取自map1。

5. Stream.of()

通過Stream.of()方法不需要藉助其他stream就可以實現map的合併。

Map<String, Employee> map3 = Stream.of(map1, map2)
  .flatMap(map -> map.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName())));

首先將map1和map2的元素合併爲同一個流,然後再轉成map。通過使用v1的id和v2的name來解決重複key的問題。

map3的運行打印結果如下:

6. Simple Streaming

我們還可以藉助stream的管道操作來實現map合併。

Map<String, Employee> map3 = map2.entrySet()
  .stream()
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName()),
  () -> new HashMap<>(map1)));

結果如下:

{John=Employee{id=8, name='John'}, 
Annie=Employee{id=22, name='Annie'}, 
George=Employee{id=2, name='George'}, 
Henry=Employee{id=1, name='Henry'}}

7. StreamEx

我們還可以使Stream API 的增強庫

如果您採用種方式,請引入一下依賴

<dependency>
    <groupId>one.util</groupId>
    <artifactId>streamex</artifactId>
    <version>0.6.5</version>
</dependency>
Map<String, Employee> map3 = EntryStream.of(map1)
  .append(EntryStream.of(map2))
  .toMap((e1, e2) -> e1);

注意 (e1, e2) -> e1 表達式來處理重複key的問題,如果沒有該表達式依然會報IllegalStateException異常。

結果:

{George=Employee{id=2, name='George'}, 
John=Employee{id=8, name='John'}, 
Annie=Employee{id=22, name='Annie'}, 
Henry=Employee{id=1, name='Henry'}}

8. Test源碼

public class MapTest {

    private static Map<String, Employee> map1 = new HashMap<>();
    private static Map<String, Employee> map2 = new HashMap<>();

    public static void main(String[] args) {
        Employee employee1 = new Employee(1L, "Henry");
        map1.put(employee1.getName(), employee1);
        Employee employee2 = new Employee(22L, "Annie");
        map1.put(employee2.getName(), employee2);
        Employee employee3 = new Employee(8L, "John");
        map1.put(employee3.getName(), employee3);
        Employee employee4 = new Employee(2L, "George");
        map2.put(employee4.getName(), employee4);
        Employee employee5 = new Employee(3L, "Henry");
        map2.put(employee5.getName(), employee5);

        // merge
        Map<String, Employee> map3 = new HashMap<>(map1);
        map2.forEach(
                (key, value)
                        -> map3.merge(key, value, (v1, v2) -> new Employee(v2.getId(),v2.getName())));
        System.out.println(map3);

        // concat
        Map<String, Employee> result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (value1, value2) -> new Employee(value2.getId(), value1.getName())));
        System.out.println(result);

        // of
        Map<String, Employee> map4 = Stream.of(map1, map2)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> new Employee(v1.getId(), v2.getName())));
        System.out.println(map4);

        // Simple Streaming
        Map<String, Employee> map5 = map2.entrySet()
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> new Employee(v1.getId(), v2.getName()),
                        () -> new HashMap<>(map1)));
        System.out.println(map5);
        
        // StreamEx
        Map<String, Employee> map6 = EntryStream.of(map1)
                .append(EntryStream.of(map2))
                .toMap((e1, e2) -> e1);
        System.out.println(map6);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章