Java Optional的映射,用户希望将函数应用到 Optional
实例的集合,但前提是 Optional
实例包含值。使用 Optional
类定义的 map
方法。假设存在一个包含员工 ID 值的列表,我们希望检索相应的员工实例集合。如果 findEmployeeById
方法具有以下签名,则搜索所有员工后将返回一个 Optional
实例的集合,其中某些实例可能为空。
Java Optional的映射 问题描述
用户希望将函数应用到 Optional
实例的集合,但前提是 Optional
实例包含值。
Java Optional的映射 解决方案
使用 Optional
类定义的 map
方法。
Java Optional的映射 具体实例
假设存在一个包含员工 ID 值的列表,我们希望检索相应的员工实例集合。如果 findEmployeeById
方法具有以下签名,则搜索所有员工后将返回一个 Optional
实例的集合,其中某些实例可能为空。例 6-20 显示了如何筛掉为空的 Optional
。
public Optional<Employee> findEmployeeById(int id)
例 6-20 根据 ID 查找
Employee
(使用Stream.map
方法)
public List<Employee> findEmployeesByIds(List<Integer> ids) {
return ids.stream()
.map(this::findEmployeeById) ➊
.filter(Optional::isPresent)) ➋
.map(Optional::get) ➌
.collect(Collectors.toList());
}
❶ Stream<Optional<Employee>>
❷ 删除空的 Optional
❸ 检索确定存在的值
如上所示,第一个 map
操作的结果是一个由 Optional
构成的流,每个 Optional
要么包含一名员工,要么为空。为提取包含的值,我们很自然会想到调用 get
方法。但必须注意,除非确定值存在,否则不要调用 get
方法。为避免出现错误,我们采用 filter
方法,它传入 Optional::isPresent
作为谓词以删除所有空 Optional
,然后通过第二个 map
操作(传入 Optional::get
)将各个 Optional
映射到它们所包含的值。
本例使用了 Stream
接口定义的 map
方法,而 Optional
类同样定义有 map
方法,其签名为:
<U> Optional<U> map(Function<? super T,? extends U> mapper)
Optional.map
方法传入 Function
作为参数。如果 Optional
不为空,map
方法将提取包含的值并应用给定的函数,然后返回一个包含结果的 Optional
;如果 Optional
为空,map
方法将返回一个空的 Optional
。
我们使用 Optional.map
方法重写例 6-20 的查找操作,结果如例 6-21 所示。
例 6-21 根据 ID 查找
Employee
(使用Optional.map
方法)
public List<Employee> findEmployeesByIds(List<Integer> ids) {
return ids.stream()
.map(this::findEmployeeById) ➊
.flatMap(optional ->
optional.map(Stream::of) ➋
.orElseGet(Stream::empty)) ➌
.collect(Collectors.toList());
}
❶ Stream<Optional<Employee>>
❷ 将非空 Optional<Employee>
转换为 Optional<Stream<Employee>>
❸ 从 Optional
中提取 Stream<Employee>
如果包含员工的 Optional
不为空,则在包含的值上调用 Stream::of
,将其转换为该值的一个单元素流(one-element stream)并包装在 Optional
中,否则返回一个空的 Optional
。
在本例中,如果根据某个员工 ID 找到了相应的员工,那么 findEmployeeById
方法将返回该值的 Optional<Employee>
,optional.map(Stream::of)
方法随后返回一个 Optional
,它包含存储该员工信息的单元素流,由此得到 Optional<Stream<Employee>>
。接下来,orElseGet
方法将包含的值提取出来,生成 Stream<Employee>
。
如果 findEmployeeById
方法返回为空的 Optional
,optional.map(Stream::of)
方法同样将返回一个空的 Optional
,而 orElseGet(Stream::empty)
方法将返回一个空的流。
由此得到 Stream<Employee>
元素与空流的组合,Stream.flatMap
方法的真正用途就在于此。仅对非空流而言,Stream.flatMap
方法将所有内容简化为 Stream<Employee>
,因此 collect
方法可以将非空流作为员工列表返回。
相应的过程如图 6-1 所示。
图 6-1:Optional.map
和 Optional.flatMap
操作
Optional.map
是一种或许有助于简化流处理代码的便捷 5 方法。对不熟悉 flatMap
操作的开发人员而言,之前讨论的 filter/map
方案显然更为直观,且得到的结果并无二致。
5至少其设计思路如此。
当然,我们可以在 Optional.map
方法中使用任何所需的函数。Javadoc 详细描述了如何将名称转换为文件输入流,其他应用请参见范例Optional的映射 。
此外,Java 9 为 Optional
类引入了一个名为 stream
的新方法。如果 Optional
不为空,stream
方法将返回一个包装包含值的单元素流,否则返回一个空流。详见范例新增的Optional方法。