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中找到。
作者:Rodrigo Graciano
译者:李东

作者:锅外的大佬
链接:https://juejin.im/post/5d06f11b5188252a71629c7b
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。