详解Java8中Collectors.toMap()

作者 | 2020年7月23日

在本教程中,我们将学习Collectors.toMap()方法,我们使用该方法来把Stream转为Map。

对于下面涉及到的所有示例,我们将使用一个书籍列表作为演示。

2. List转Map

我们先从简单的开始,把List转为Map。

Book类的定义如下:

class Book {
    private String name;
    private int releaseYear;
    private String isbn;

    // getters and setters
}

然后创建一个List对象:

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)

我们可以使用不同的策略来获取Map中的Key与Value:

public Map<String, String> listToMap(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName));
}

3. 解决键冲突

上面的例子在理想的情况下可以工作的很好,但是如果有重复的键怎么办?

我们假设我们使用每本书的出版年份来作为Key:

public Map<Integer, Book> listToMapWithDupKeyError(List<Book> books) {
    return books.stream().collect(
      Collectors.toMap(Book::getReleaseYear, Function.identity()));
}

在此时,由于Key会出现冲突,所以上面的代码会抛出IllegalStateException异常。

要解决该异常我们可以使用另一个带有mergeFunction参数的方法:

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

我们来学习一下mergeFunction参数怎么用,下面的代码中,如果Key发生冲突,则会保留第一次出现的。

public Map<Integer, Book> listToMapWithDupKey(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
      (existing, replacement) -> existing));
}

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));
}

4.2 有序的Map

下面的代码演示了使用mappSupplier参数返回一个TreeMap的实例:

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

5. 总结

在本文中我们快速的学习了Collectors.toMap()方法的使用,它允许我们创建一个新的Map。我们还学习了如果解决Key冲突以及如何返回不同类型的Map实现。

发表评论

电子邮件地址不会被公开。 必填项已用*标注