使用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);
請注意,我們在地圖中為employee1
和employee5
條目具有相同的鍵,我們將在以後使用。
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
對象具有來自map1
的id
,並且從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())));
首先,我們將map1
和map2
轉換為單個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()
管道來組合地圖條目。下面的代碼段演示瞭如何通過忽略重複的條目來添加map2
和map1
的條目:
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
庫。
簡而言之, StreamEx
是Stream
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上找到討論期間使用的代碼。