Java 使用flatMap与map方法

Java 使用flatMap与map方法,用户希望以某种方式转换流中的元素,但不确定该使用 map 还是 flatMap 方法。如果需要将每个元素转换为一个值,则使用 Stream.map 方法;如果需要将每个元素转换为多个值,且需要将生成的流“展平”,则使用 Stream.flatMap 方法。

Java 使用flatMap与map方法 问题描述

用户希望以某种方式转换流中的元素,但不确定该使用 map 还是 flatMap 方法。

Java 使用flatMap与map方法 解决方案

如果需要将每个元素转换为一个值,则使用 Stream.map 方法;如果需要将每个元素转换为多个值,且需要将生成的流“展平”,则使用 Stream.flatMap 方法。

Java 使用flatMap与map方法 具体实例

mapflatMap 方法均传入 Function 作为参数。map 方法的签名如下:

<R> Stream<R> map(Function<? super T,? extends R> mapper)

Function 传入一个输入,并将其转换为一个输出。map 方法则将一个 T 类型的输入转换为一个 R 类型的输出。
我们创建一个由顾客名和 Order 集合构成的 Customer 类。为简单起见,Order 类只包含一个整数 ID。例 3-54 展示了 Customer 类和 Order 类。

例 3-54 一对多关系

public class Customer {
    private String name;
    private List<Order> orders = new ArrayList<>();

    public Customer(String name) {
        this.name = name;
    }

    public String getName() { return name; }
    public List<Order> getOrders() { return orders; }

    public Customer addOrder(Order order) {
        orders.add(order);
        return this;
    }
}

public class Order {
    private int id;

    public Order(int id) {
        this.id = id;
    }

    public int getId() { return id; }
}

接下来,我们创建若干新顾客并添加一些订单,如例 3-55 所示。

例 3-55 客户与订单示例

Customer sheridan = new Customer("Sheridan");
Customer ivanova = new Customer("Ivanova");
Customer garibaldi = new Customer("Garibaldi");

sheridan.addOrder(new Order(1))
        .addOrder(new Order(2))
        .addOrder(new Order(3));
ivanova.addOrder(new Order(4))
        .addOrder(new Order(5));

List<Customer> customers = Arrays.asList(sheridan, ivanova, garibaldi);

当输入参数和输出类型之间存在一一对应的关系时,将执行 map 操作。可以将顾客映射到他们的姓名并打印,如例 3-56 所示。

例 3-56 将顾客映射到他们的姓名

customers.stream()                     ➊
        .map(Customer::getName)        ➋
        .forEach(System.out::println); ➌

Stream<Customer>
Stream<String>
❸ 谢里登、伊万诺娃、加里波第
如果将顾客映射到订单而不是他们的姓名,就得到了一个集合的集合,如例 3-57 所示。

例 3-57 将顾客映射到订单

customers.stream()
        .map(Customer::getOrders)                       ➊
        .forEach(System.out::println);                  ➋

customers.stream()
        .map(customer -> customer.getOrders().stream()) ➌
        .forEach(System.out::println);

Stream<List<Order>>
[Order{id=1}, Order{id=2}, Order{id=3}], [Order{id=4}, Order{id=5}], []
Stream<Stream<Order>>
map 操作的结果为 Stream<List<Order>>,其最后一个列表为空。如果在订单列表中调用 stream 方法,则结果为 Stream<Stream<Order>>,其最后一个内部流(inner stream)为空流。
flatMap 方法的作用就在于此,其签名如下:

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

对于每个泛型参数 T,函数生成的是 Stream<R> 而不仅仅是 R。之后,flatMap 方法从各个流中删除每个元素并将它们添加到输出,从而“展平”生成的流。

flatMap 方法的 Function 参数传入一个泛型输入参数,但生成的输出类型为 Stream

flatMap 方法的应用如例 3-58 所示。

例 3-58 对顾客订单应用 flatMap 方法

customers.stream()                                          ➊
        .flatMap(customer -> customer.getOrders().stream()) ➋
        .forEach(System.out::println);                      ➌

Stream<Customer>
Stream<Order>
Order{id=1}, Order{id=2}, Order{id=3}, Order{id=4}, Order{id=5}
flatMap 操作的结果为 Stream<Order>。由于它已被“展平”,无须再担心嵌套流(nested stream)。
flatMap 方法有关的两个重要概念应予注意:

  • 方法参数 Function 产生一个输出值流;

  • 生成的元素被“展平”为一个新的流。

将上述两点谨记在心,就能体会到 flatMap 方法的有用之处。
最后需要指出的是,Java 8 引入的 Optional 类同样定义了 mapflatMap 方法,详细讨论请参见范例Optional.flatMap与Optional.map方法和范例Optional的映射

赞(4)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例