Java 8 Streams peek() API – Java 8 Streams peek() API

最后修改: 2019年 1月 16日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The Java Stream API introduces us to a powerful alternative for processing data.

Java Stream API向我们介绍了一种处理数据的强大替代方案。

In this short tutorial, we’ll focus on peek(), an often misunderstood method.

在这个简短的教程中,我们将关注peek(),一个经常被误解的方法。

2. Quick Example

2.快速实例

Let’s get our hands dirty and try to use peek(). We have a stream of names, and we want to print them to the console.

让我们动手尝试一下使用peek()。我们有一个名字流,我们想把它们打印到控制台。

Since peek() expects a Consumer<T> as its only argument, it seems like a good fit, so let’s give it a try:

由于peek()期望一个Consumer<T>作为其唯一的参数,它似乎很适合,所以让我们试一试。

Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.peek(System.out::println);

However, the snippet above produces no output. To understand why, let’s do a quick refresher on aspects of the stream lifecycle.

然而,上面的片段没有产生任何输出。为了了解原因,让我们快速复习一下流生命周期的各个方面。

3. Intermediate vs. Terminal Operations

3.中级业务与终端业务

Recall that streams have three parts: a data source, zero or more intermediate operations, and zero or one terminal operation.

回顾一下,流有三个部分:一个数据源,零或多个中间操作,以及零或一个终端操作。

The source provides the elements to the pipeline.

源头向管道提供元素。

Intermediate operations get elements one-by-one and process them. All intermediate operations are lazy, and, as a result, no operations will have any effect until the pipeline starts to work.

中间操作逐一获取元素并处理它们。所有的中间操作都是懒惰的,因此,在管道开始工作之前,任何操作都不会产生任何影响。

Terminal operations mean the end of the stream lifecycle. Most importantly for our scenario, they initiate the work in the pipeline.

终端操作意味着流生命周期的结束。对我们的方案来说,最重要的是,它们启动了管道中的工作

4. peek() Usage

4.peek()用法

The reason peek() didn’t work in our first example is that it’s an intermediate operation and we didn’t apply a terminal operation to the pipeline. Alternatively, we could have used forEach() with the same argument to get the desired behavior:

在我们的第一个例子中,peek()不工作的原因是它是一个中间操作,而我们没有对管道应用一个终端操作。另外,我们可以使用forEach(),用同样的参数来获得所需的行为。

Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.forEach(System.out::println);

peek()‘s Javadoc page says: “This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline“.

peek()Javadoc页面说:”这个方法的存在主要是为了支持调试,即你想看到元素在管道中流过某个点时的情况“。

Let’s consider this snippet from the same Javadoc page:

让我们考虑一下同一Javadoc页面上的这个片段。

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());

It demonstrates, how we observe the elements that passed each operation.

它展示了,我们如何观察通过每个操作的元素。

On top of that, peek() can be useful in another scenario: when we want to alter the inner state of an element. For example, let’s say we want to convert all user’s name to lowercase before printing them:

除此之外,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);

Alternatively, we could have used map(), but peek() is more convenient since we don’t want to replace the element.

另外,我们可以使用map(),但是peek()更方便,因为我们不想替换这个元素。

5. Conclusion

5.总结

In this short tutorial, we saw a summary of the stream lifecycle to understand how peek() works. We also saw two everyday use cases when using peek() is the most straightforward option.

在这个简短的教程中,我们看到了对流生命周期的总结,以了解peek()如何工作。我们还看到了两个日常用例,当使用peek()是最直接的选择。

And as usual, the examples are available over on GitHub.

和往常一样,这些例子可以在GitHub上找到