Java 8 Collectors to Map

1. 介紹

在本教程中,我們將討論Collectors類的toMap()方法。我們使用它將流收集到一個Map實例中。

對于本教程中涉及的所有示例,我們將使用圖書列表作為數據源,并將其轉換為不同的Map實現。

2. List 轉換 Map

我們將從最簡單的情況開始,將List 轉換 Map

Book類定義如下:

class Book {
    private String name;
    private int releaseYear;
    private String isbn;
    //getters and setters
}
復制代碼

接著,我們將創建一個List<Book>來驗證我們的代碼:

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)
復制代碼

使用Collectors.toMap(), 我們將會得到一個Map<String,String>,其中key是isbn的值,value為name的值。

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);//true
}
復制代碼

3. 解決 Key 的沖突

上面的例子運行得很好,但是如果有一個重復的key會發生什么呢?

讓我們來想象一下,我們將每本圖書的出版年份作為key,轉換到Map<Integer, Book>中。

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);
}
復制代碼

要解決這個問題,我們需要使用另一種toMap()方法,附加一個參數,mergeFunction:

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper,
  BinaryOperator<U> mergeFunction)
復制代碼

讓我們引入一個merge函數,它表明,在發生沖突的情況下,我們保留現有的元素:

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. List 轉換 SortedMap

最后,讓我們看看如何返回一個排序后的Map。為此,我們需要對List<Book>進行排序,并使用TreeMap作為mapSupplier參數:

public TreeMap<String, Book> listToSortedMap(List<Book> books) {
    return books.stream() 
      .sorted(Comparator.comparing(Book::getName))
      .collect(Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new));
}
復制代碼

上面的代碼將List<Book>按照書名進行排序,然后將結果收集到TreeMap<String, Book>中:

@Test
public void whenMapisSorted() {
    assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals("The Fellowship of the Ring"));
}
復制代碼

5. 結論

在本文中,我們研究了Collectors類的toMap()方法。它允許我們從一個流創建一個新的Map。我們還學習了如何解決key沖突和創建不同的Map實現。

代碼可以在GitHub中找到。

原文鏈接:www.baeldung.com/java-collec…

作者:Rodrigo Graciano

譯者:李東

 


作者:鍋外的大佬
鏈接:https://juejin.im/post/5d06f11b5188252a71629c7b
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
posted @ 2019-06-17 09:49 鍋外的大佬 閱讀(...) 評論(...) 編輯 收藏
四川金7乐历史开奖号码查询