使用Java 8合併兩個HashMap

1.簡介

在本快速教程中,我們將演示如何使用Java 8功能合併兩個HashMap

更具體地說,我們將研究不同的合併方案,包括具有重複條目的HashMap。

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;



    // constructor, 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條目具有相同的鍵,我們將在以後使用。

3. Map.merge()

Java 8將新的merge()函數添加到java.util.Map接口

merge()函數的工作方式如下:如果指定的鍵尚未與某個值關聯或該值為null,則它將鍵與給定的值關聯。

否則,它將用給定的重映射函數的結果替換該值。如果重新映射函數的結果為null,則將其刪除。

首先,我們通過複製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())));

讓我們運行程序並打印map3的內容:

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具有先前HashMap條目的所有元素。具有重複鍵的條目已合併為一個條目

此外,我們注意到最後一個條目的Employee對象具有來自map1id ,並且從map2選取了值。

這是因為我們在合併功能中定義了規則:

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

4. Stream.concat()

Java 8中的Stream API還可以為我們的問題提供簡單的解決方案。首先,我們需要將Map實例組合到一個Stream 。這正是Stream.concat()操作的作用:

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

在這裡,我們將地圖條目集作為參數傳遞。接下來,我們需要將結果收集到一個新的Map 。為此,我們可以使用Collectors.toMap()

Map<String, Employee> result = combined.collect(

 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

因此,收集器將使用我們地圖的現有鍵和值。但是,這種解決方案遠非完美。一旦我們的收集器遇到帶有重複鍵的條目,它將拋出IllegalStateException

要解決此問題,我們只需在收集器中添加第三個“合併” lambda參數:

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

每當檢測到重複鍵時,它將使用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'}

如我們所見,具有鍵“Henry”的重複條目被合併為新的鍵值對,其中新的Employee的id是從map2 ,值是從map1

5. Stream.of()

要繼續使用Stream API,我們可以在Stream.of()的幫助下將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())));

首先,我們將map1map2轉換為單個stream 。接下來,我們將流轉換為地圖。如我們所見, toMap()的最後一個參數是一個合併函數。它通過從v1條目中選擇id字段,並從v2選擇名稱來解決重複鍵問題。

運行程序後打印的map3實例:

George=Employee{id=2, name='George'}

 John=Employee{id=8, name='John'}

 Annie=Employee{id=22, name='Annie'}

 Henry=Employee{id=1, name='Henry'}

6.簡單的Stream流

此外,我們可以使用stream()管道來組合地圖條目。下面的代碼段演示瞭如何通過忽略重複的條目來添加map2map1的條目:

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

除了JDK提供的解決方案之外,我們還可以使用流行的StreamEx庫。

簡而言之, StreamExStream API的增強功能,並提供了許多其他有用的方法。我們將使用**EntryStream實例對鍵值對進行操作**:

Map<String, Employee> map3 = EntryStream.of(map1)

 .append(EntryStream.of(map2))

 .toMap((e1, e2) -> e1);

這個想法是將我們的地圖流合併為一個。然後,我們將條目收集到新的map3實例中。值得一提的是(e1, e2) -> e1表達式,因為它有助於定義處理重複鍵的規則。沒有它,我們的代碼將拋出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.總結

在這篇簡短的文章中,我們學習了Java 8中合併地圖的不同方法。更具體地說,我們使用了Map.merge(), Stream API, StreamEx

與往常一樣,可以在GitHub上找到討論期間使用的代碼。