点击左上角蓝字,关注“锅外的大佬”
专注分享国外最新技术内容

1. 介绍
在本教程中,我们将讨论 Collectors类的 toMap()方法。我们使用它将流收集到一个 Map实例中。
对于本教程中涉及的所有示例,我们将使用图书列表作为数据源,并将其转换为不同的 Map实现。

2. List 转换 Map

我们将从最简单的情况开始,将 List 转换 Map
Book类定义如下:
  1. classBook{
  2. privateString name;
  3. privateint releaseYear;
  4. privateString isbn;
  5. //getters and setters
  6. }
接着,我们将创建一个 List<Book>来验证我们的代码:
  1. List<Book> bookList =newArrayList<>();
  2. bookList.add(newBook("The Fellowship of the Ring",1954,"0395489318"));
  3. bookList.add(newBook("The Two Towers",1954,"0345339711"));
  4. bookList.add(newBook("The Return of the King",1955,"0618129111"));
对于这个场景,我们将使用以下重载的 toMap()方法:
  1. Collector<T,?,Map<K,U>> toMap(Function<?super T,?extends K> keyMapper,
  2. Function<?super T,?extends U> valueMapper)
使用 Collectors.toMap(), 我们将会得到一个 Map<String,String>,其中key是isbn的值,value为name的值。
  1. publicMap<String,String> listToMap(List<Book> books){
  2. return books.stream().collect(Collectors.toMap(Book::getIsbn,Book::getName));
  3. }
我们使用单元测试来验证一下代码:
  1. @Test
  2. publicvoid whenConvertFromListToMap(){
  3. assertTrue(convertToMap.listToMap(bookList).size()==3);//true
  4. }

3. 解决 Key 的冲突

上面的例子运行得很好,但是如果有一个重复的key会发生什么呢?
让我们来想象一下,我们将每本图书的出版年份作为key,转换到 Map<Integer,Book>中。
  1. publicMap<Integer,Book> listToMapWithDupKeyError(List<Book> books){
  2. return books.stream().collect(Collectors.toMap(Book::getReleaseYear,Function.identity()));
  3. }
鉴于我们上面的例子,我们会看到一个 IllegalStateException的异常:
  1. @Test(expected =IllegalStateException.class)
  2. publicvoid whenMapHasDuplicateKey_without_merge_function_then_runtime_exception(){
  3. convertToMap.listToMapWithDupKeyError(bookList);
  4. }
要解决这个问题,我们需要使用另一种 toMap()方法,附加一个参数,mergeFunction:
  1. Collector<T,?, M> toMap(Function<?super T,?extends K> keyMapper,
  2. Function<?super T,?extends U> valueMapper,
  3. BinaryOperator<U> mergeFunction)
让我们引入一个merge函数,它表明,在发生冲突的情况下,我们保留现有的元素:
  1. publicMap<Integer,Book> listToMapWithDupKey(List<Book> books){
  2. return books.stream().collect(Collectors.toMap(Book::getReleaseYear,Function.identity(),
  3. (existing, replacement)-> existing));
  4. }
或者,换句话说,我们获得了未发生异常的元素:
  1. @Test
  2. publicvoid whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision(){
  3. Map<Integer,Book> booksByYear = convertToMap.listToMapWithDupKey(bookList);
  4. assertEquals(2, booksByYear.size());
  5. assertEquals("0395489318", booksByYear.get(1954).getIsbn());
  6. }

4. 其他Map类型

默认情况下, toMap()方法将返回一个 HashMap。 但是我们也可以返回不同的 Map实现。
  1. Collector<T,?, M> toMap(Function<?super T,?extends K> keyMapper,
  2. Function<?super T,?extends U> valueMapper,
  3. BinaryOperator<U> mergeFunction,
  4. Supplier<M> mapSupplier)
其中mapSupplier是一个函数,它返回一个新的、带有结果的空 Map

4.1. List 转换 ConcurrentMap

让我们以上面的例子为例,添加一个mapSupplier函数来返回一个 ConcurrentHashMap:
  1. publicMap<Integer,Book> listToConcurrentMap(List<Book> books){
  2. return books.stream().collect(Collectors.toMap(Book::getReleaseYear,Function.identity(),
  3. (o1, o2)-> o1,ConcurrentHashMap::new));
  4. }
下面我们测试一下
  1. @Test
  2. publicvoid whenCreateConcurrentHashMap(){
  3. assertTrue(convertToMap.listToConcurrentMap(bookList)instanceofConcurrentHashMap);
  4. }

4.2. List 转换 SortedMap

最后,让我们看看如何返回一个排序后的Map。为此,我们需要对 List<Book>进行排序,并使用 TreeMap作为mapSupplier参数:
  1. publicTreeMap<String,Book> listToSortedMap(List<Book> books){
  2. return books.stream()
  3. .sorted(Comparator.comparing(Book::getName))
  4. .collect(Collectors.toMap(Book::getName,Function.identity(),(o1, o2)-> o1,TreeMap::new));
  5. }
上面的代码将 List<Book>按照书名进行排序,然后将结果收集到 TreeMap<String,Book>中:
  1. @Test
  2. publicvoid whenMapisSorted(){
  3. assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals("The Fellowship of the Ring"));
  4. }

5. 结论

在本文中,我们研究了 Collectors类的 toMap()方法。它允许我们从一个流创建一个新的 Map。我们还学习了如何解决key冲突和创建不同的 Map实现。
代码可以在GitHub中找到。
原文链接:https://www.baeldung.com/java-collectors-tomap
作者:Rodrigo Graciano
译者:李东
推荐阅读:
上篇好文:

文章对你是否有帮助呢?
别忘记右上角按钮分享给更多人哦
~



点击在看,和我一起帮助更多开发者!
继续阅读
阅读原文