Java 8收集器toMap
1.簡介
在本快速教程中,我們將討論Collectors
類的toMap()
方法。我們將使用它將Stream
收集到一個Map
實例中。
對於此處涵蓋的所有示例,我們將使用書籍清單作為起點並將其轉換為不同的Map
實現。
2. List
轉變爲Map
我們將從最簡單的情況開始,將List
轉換為Map
。
我們的Book
類定義為:
class Book {
private String name;
private int releaseYear;
private String isbn;
// getters and setters
}
我們將創建一本書清單來驗證我們的代碼:
List<Book> bookList = new ArrayList<>();
bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318"));
bookList.add(new Book("The Two Towers", 1954, "0345339711"));
bookList.add(new Book("The Return of the King", 1955, "0618129111"));
在這種情況下,我們將使用toMap()
方法的以下重載:
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
使用toMap
,我們可以指示如何獲取地圖的鍵和值的策略:
public Map<String, String> listToMap(List<Book> books) {
return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName));
}
我們可以輕鬆地驗證它是否可以與以下產品一起使用:
@Test
public void whenConvertFromListToMap() {
assertTrue(convertToMap.listToMap(bookList).size() == 3);
}
3.解決關鍵衝突
上面的示例效果很好,但是如果有重複的密鑰會發生什麼?
假設我們按每Book
的發行年份確定了Map
的關鍵點:
public Map<Integer, Book> listToMapWithDupKeyError(List<Book> books) {
return books.stream().collect(
Collectors.toMap(Book::getReleaseYear, Function.identity()));
}
鑑於我們之前的書籍清單,我們會看到一個IllegalStateException
:
@Test(expected = IllegalStateException.class)
public void whenMapHasDuplicateKey_without_merge_function_then_runtime_exception() {
convertToMap.listToMapWithDupKeyError(bookList);
}
要解決此問題,我們需要使用帶有其他參數的不同方法mergeFunction
:
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
讓我們介紹一個合併函數,該函數指示在發生碰撞的情況下,我們保留現有條目:
public Map<Integer, Book> listToMapWithDupKey(List<Book> books) {
return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
(existing, replacement) -> existing));
}
或者換句話說,我們獲得了第一手的行為:
@Test
public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() {
Map<Integer, Book> booksByYear = convertToMap.listToMapWithDupKey(bookList);
assertEquals(2, booksByYear.size());
assertEquals("0395489318", booksByYear.get(1954).getIsbn());
}
4.其他Map類型
默認情況下, toMap()
方法將返回HashMap
。
但是我們可以返回不同的Map
實現嗎?答案是肯定的:
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
其中的mapSupplier
是一個函數,該函數返回帶有結果的新的空Map
。
4.1。 List
到ConcurrentMap
讓我們以與上面相同的示例為例,並添加一個mapSupplier
函數以返回ConcurrentHashMap:
public Map<Integer, Book> listToConcurrentMap(List<Book> books) {
return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
(o1, o2) -> o1, ConcurrentHashMap::new));
}
讓我們繼續測試我們的代碼:
@Test
public void whenCreateConcurrentHashMap() {
assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap);
}
4.2排序Map
最後,讓我們看看如何返回排序後的地圖。為此,我們將使用TreeMap
作為mapSupplier
參數。
因為默認情況下, TreeMap
是根據其鍵的自然順序來排序的,所以我們不必自己對books
進行顯式排序:
public TreeMap<String, Book> listToSortedMap(List<Book> books) {
return books.stream()
.collect(
Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new));
}
因此,在本例中,返回的TreeMap
將按書名的字母順序排序:
@Test
public void whenMapisSorted() {
assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals(
"The Fellowship of the Ring"));
}
5.結論
在本文中,我們研究了Collectors
類的toMap()
方法。它允許我們從Stream
創建一個新的Map
。我們還學習瞭如何解決關鍵衝突並創建不同的地圖實現。
與往常一樣,代碼可以在GitHub上獲得。