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。 ListConcurrentMap

讓我們以與上面相同的示例為例,並添加一個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上獲得