Java8 Collectors to Map


点击左上角蓝字,关注“锅外的大佬”640?wx_fmt=gif

专注分享国外最新技术内容


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中找到。

原文链接:https://www.baeldung.com/java-collectors-tomap

作者:Rodrigo Graciano

译者:李东

推荐阅读:

如何配置Spring Boot Tomcat

上篇好文:

Java系统中拆分同步和异步


文章对你是否有帮助呢?

别忘记右上角按钮分享给更多人哦smiley_63.png~


640?wx_fmt=png


点击在看,和我一起帮助更多开发者!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值