4.3 将线性集合添加到映射
问题
用户希望将对象集合添加到 Map,其中键为某种对象属性,值为对象本身。
方案
使用 Collectors 类定义的 toMap 方法以及 Function 接口定义的 identity 方法。
讨论
这是一个简短且非常集中的用例,但本节讨论的解决方案可能为实际开发提供很大便利。
假设存在一个由 Book 实例构成的 List。Book 是一个简单的 POJO,由 ID、书名、价格参数构成。Book 类的简单形式如例 4-13 所示。
例 4-13
Book类(描述图书的简单 POJO)
- public class Book {
- private int id;
- private String name;
- private double price;
- // 其他方法
- }
此外,假设存在一个由 Book 实例构成的集合,如例 4-14 所示。
例 4-14 图书集合
- List<Book> books = Arrays.asList(
- new Book(1, "Modern Java Recipes", 49.99),
- new Book(2, "Java 8 in Action", 49.99),
- new Book(3, "Java SE8 for the Really Impatient", 39.99),
- new Book(4, "Functional Programming in Java", 27.64),
- new Book(5, "Making Java Groovy", 45.99)
- new Book(6, "Gradle Recipes for Android", 23.76)
- );
很多情况下,我们需要的可能是 Map 而非 List,Map 的键为图书 ID,值为图书本身。借由 Collectors.toMap 方法,很容易就能将图书添加到 Map,例 4-15 显示了两种不同的方案。
例 4-15 将图书添加到
Map
Map<Integer, Book> bookMap = books.stream().collect(Collectors.toMap(Book::getId, b -> b)); ➊bookMap = books.stream().collect(Collectors.toMap(Book::getId, Function.identity())); ➋
❶ lambda 标识:给定一个元素并返回
❷ 静态方法 Function.identity 可以实现同样的目的
toMap 方法传入两个 Function 实例作为参数,根据所提供的对象,两个函数分别生成键和值。在本例中,键由 Book::getId 映射,值为图书本身。
可以看到,第一个 toMap 方法传入两个参数,一个是映射到键的 getId,另一个是返回参数的显式 lambda 表达式。第二个 toMap 方法通过静态方法 Function.identity 实现相同的目的。
两种静态
identity方法静态方法
Function.identity的签名如下:
static <T> Function<T,T> identity()它在 Java 标准库中的实现如例 4-16 所示。
例 4-16
Function.identity方法
- static <T> Function<T, T> identity() {
- return t -> t;
- }
UnaryOperator接口是Function的子接口,但无法重写静态方法。根据 Javadoc 的描述,UnaryOperator接口也声明了一个称为identity的静态方法:
static <T> UnaryOperator<T> identity()
UnaryOperator.identity方法在 Java 标准库中的实现与Function.identity方法基本相同,如例 4-17 所示。例 4-17
UnaryOperator.identity方法
- static <T> UnaryOperator<T> identity() {
- return t -> t;
- }
二者的区别仅在于调用方式(不同的接口名)和相应的返回类型有所不同。使用哪种
identity方法均可,这里将两种方法列出供读者参考。无论是提供显式的 lambda 表达式抑或使用静态方法,只是编程风格不同而已。两种方案都能很容易地将集合值添加到
Map,其中键为对象的属性,值为对象本身。
另见
有关 Function 接口、一元运算符与二元运算符的讨论请参见范例 2.4。
