本文介绍了Java 8 流peek()api,因为其通常被误解或误用。
首先从示例开始,在控制台中打印字符串流,因为peek需要Consumer 作为参数,尝试代码如下:
Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.peek(System.out::println);
但上述代码没有输出,这是为什么?
回顾下流,其有三个部分,数据源,零个或多个中间操作,零个或多个终止操作。
数据源提供元素给流程。中间操作一个一个获取元素并处理,所有中间操作是延迟执行,直到流程开始执行才开始真正执行。终止操作意味着流生命周期结束,更重要的是终止操作启动流程开始工作。从而提升执行效率,避免中间操作做无用功,底层实现了熔断机制,只有需要才执行。
因此,上面代码因为没有终止操作,实际并没有执行,一般流都需要有终止操作启动流程执行。
首先我们修改上面的示例,改用forEach:
Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.forEach(System.out::println);
peek官方文档说明:peek方法主要用于调试,在流程某个步骤查看执行元素情况。
根据官网提示在看个示例:
Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
这个示例可以观察到元素流过每个操作的值。除此之外,peek()在另一个场景中也很有用:改变一个元素的内部状态。例如我们想要在打印所有用户名之前将其转换为小写:
Stream<User> userStream = Stream.of(new User("Alice"), new User("Bob"), new User("Chuck"));
userStream.peek(u -> u.setName(u.getName().toLowerCase()))
.forEach(System.out::println);
当然也可以使用map,当peek更方便,因为无需替换元素。
本文我们从流程的生命周期角度理解peek,通过示例介绍了两个典型使用场景。