Java Optional的映射

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 方法返回为空的 Optionaloptional.map(Stream::of) 方法同样将返回一个空的 Optional,而 orElseGet(Stream::empty) 方法将返回一个空的流。
由此得到 Stream<Employee> 元素与空流的组合,Stream.flatMap 方法的真正用途就在于此。仅对非空流而言,Stream.flatMap 方法将所有内容简化为 Stream<Employee>,因此 collect 方法可以将非空流作为员工列表返回。
相应的过程如图 6-1 所示。

图 6-1:Optional.mapOptional.flatMap 操作
Optional.map 是一种或许有助于简化流处理代码的便捷 5 方法。对不熟悉 flatMap 操作的开发人员而言,之前讨论的 filter/map 方案显然更为直观,且得到的结果并无二致。
5至少其设计思路如此。

当然,我们可以在 Optional.map 方法中使用任何所需的函数。Javadoc 详细描述了如何将名称转换为文件输入流,其他应用请参见范例Optional的映射
此外,Java 9 为 Optional 类引入了一个名为 stream 的新方法。如果 Optional 不为空,stream 方法将返回一个包装包含值的单元素流,否则返回一个空流。详见范例新增的Optional方法

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程