【Java 8 新特性】Java Stream通过peek在调用子元素时执行方法

  • 1. Stream.peek() : 是一个中间操作(`An Intermediate Operation`)
  • 2.peek()示例
  • 3.`peek()`并行流(`Parallel Stream`)示例
  • 4.Java 9中的`peek()`和`count()`
  • 参考文献



本页将介绍

Stream.peek方法示例。


peek方法返回由该流的元素组成的流,并对每个元素执行所提供的

Consumer操作方法。


peek是一个中间操作方法。

从javadoc中找到peek方法语法。

Stream<T> peek(Consumer<? super T> action)

参数:将Consumer作为操作方法传递。
返回:该方法返回一个新的流。

peek方法主要用于调试,以便在元素流过管道中的某个点时查看它们。

在并行流操作中,peek方法的操作可以在上游操作使元素可用的任何时间和线程中被调用。

peek方法是中间方法,将在调用终端方法时执行。但是在Java9中,对于count()这样的短路操作,peek方法中的操作不会被这些元素调用。

1. Stream.peek() : 是一个中间操作(An Intermediate Operation)

peek方法是中间方法,因此在调用终端方法之前它不会执行。让我们举个例子来看看。找到我们没有调用任何终端方法的peek方法。

Stream.of(10, 20, 30).peek(e -> System.out.println(e));

不会有输出。

现在在上面的代码中使用一个终端方法,我们将得到peek方法的输出。

Stream.of(10, 20, 30).peek(e -> System.out.println(e))
    .collect(Collectors.toList());

查看输出。

10 20 30

2.peek()示例

peek方法主要用于调试。查看例子。

示例1:我们有一个整数流。首先我们将过滤,然后调试,然后映射,然后再次调试。

Stream.of(10, 11, 12, 13)
 .filter(n -> n % 2 == 0)
 .peek(e -> System.out.println("Debug filtered value: " + e))
 .map(n -> n * 10)
 .peek(e -> System.out.println("Debug mapped value: " + e))
 .collect(Collectors.toList());

输出

Debug filtered value: 10
Debug mapped value: 100
Debug filtered value: 12
Debug mapped value: 120

示例2:在这个例子中,我们有一个重复值的列表。我们将找到不同的值并使用peek方法进行调试。

List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA");
String output = list.stream()
	.distinct()
	.peek(e -> System.out.println("Debug value: " + e))
	.collect(Collectors.joining(","));
System.out.println(output);

输出

Debug value: AA
Debug value: BB
Debug value: CC
AA,BB,CC

示例3:在这个例子中,我们将使用peek方法在列表中存储数据。

PeekDemo.java

package com.concretepage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class PeekDemo {
  public static void main(String[] args) {
	List<String> debugList = new ArrayList<>();
        List<String> names = Arrays.asList("Mahesh", "Suresh", "Mahendra");
        names.stream()
         .filter(el -> el.startsWith("M"))
         .peek(e -> debugList.add(e))
         .collect(Collectors.toList());
        System.out.println(debugList);
  }
}

输出

[Mahesh, Mahendra]

3.peek()并行流(Parallel Stream)示例

我们知道在并行流操作中,peek方法可以在上游操作使元素可用的任何时间和线程中被调用。因此peek方法接收到的元素可能会因多次运行而有所不同。
查看例子

PeekDemoParallel.java

package com.concretepage;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PeekDemoParallel {
  public static void main(String[] args) {
    List<Integer> sortedList = Stream.of(15, 10, 17, 11)
      .parallel()
      .sorted()
      .peek(e -> System.out.println("Debug: " + e))
      .collect(Collectors.toList());
    
    System.out.println("---After sorting---");
    System.out.println(sortedList);
  }
}

输出

Debug: 15
Debug: 11
Debug: 17
Debug: 10
---After sorting---
[10, 11, 15, 17]

4.Java 9中的peek()和count()

在Java9中,对于像count()这样的短路操作,peek方法中的操作不会被这些元素调用。
peek方法不向流中注入元素或从流中移除元素。
在流源中,元素的数量是已知的,count是流列表的大小,因此不需要执行管道。
查看代码

long cnt = Stream.of(10, 11, 12, 13)
  .peek(e -> System.out.println("Debug: " + e))
  .count();
System.out.println(cnt);

输出
peek将不执行,输出将只有4。

参考文献

【1】Java doc: Stream【2】Java Stream peek()