Java 利用peek方法对流进行调试,用户希望在处理流时查看流中的各个元素,根据需要,在流的流水线中调用 java.util.stream.Stream
接口定义的中间操作 peek
。流处理由一系列零个或多个中间操作构成,后跟终止操作。每个中间操作都返回一个新的流,而终止操作将返回不是流的值。
Java 利用peek方法对流进行调试 问题描述
用户希望在处理流时查看流中的各个元素。
Java 利用peek方法对流进行调试 解决方案
根据需要,在流的流水线中调用 java.util.stream.Stream
接口定义的中间操作 peek
。
Java 利用peek方法对流进行调试 具体实例
流处理由一系列零个或多个中间操作构成,后跟终止操作。每个中间操作都返回一个新的流,而终止操作将返回不是流的值。
对于流的流水线所涉及的中间操作序列,初次接触 Java 8 的用户有时候会感到困惑,因为无法在处理流时查看各个元素的值。
我们考虑这样一个简单的方法,即接受某个整数流的开始范围和结束范围,并将每个数字倍增,然后仅对能被 3 整除的结果值求和,如例 3-32 所示。
例 3-32 对整数进行倍增、筛选与求和
public int sumDoublesDivisibleBy3(int start, int end) {
return IntStream.rangeClosed(start, end)
.map(n -> n * 2)
.filter(n -> n % 3 == 0)
.sum();
}
编写一个简单的测试,验证程序可以正确执行:
@Test
public void sumDoublesDivisibleBy3() throws Exception {
assertEquals(1554, demo.sumDoublesDivisibleBy3(100, 120));
}
上述测试虽然有一定帮助,但并未提供太多有价值的信息。如果代码无法运行,很难找出症结所在。
如例 3-33 所示,我们为流水线添加一个 map
操作,传入并打印每个值,然后再次返回这些值。
例 3-33 添加标识映射以便打印
public int sumDoublesDivisibleBy3(int start, int end) {
return IntStream.rangeClosed(start, end)
.map(n -> { ➊
System.out.println(n);
return n;
})
.map(n -> n * 2)
.filter(n -> n % 3 == 0)
.sum();
}
➊ 标识映射(identity map)打印并返回每个元素
程序将打印从 start
(含)到 end
(含)的数字,每行一个数字。尽管不应在生产环境中使用上述代码,但它的确能让用户在不影响流处理的同时观察其内部操作。
这正是 Stream
接口中 peek
方法的工作原理,该方法的声明如下:
Stream<T> peek(Consumer<? super T> action)
根据 Javadoc 的描述,peek
方法“返回一个由流的元素构成的流,当元素从所生成的流中消耗时,对每个元素执行给定的操作”。由于 Consumer
仅传入一个输入而不返回任何值,所提供的 Consumer
不会对值造成破坏。
peek
方法是一种中间操作,可以根据需要多次添加,如例 3-34 所示。
例 3-34 使用多个
peek
方法
public int sumDoublesDivisibleBy3(int start, int end) {
return IntStream.rangeClosed(start, end)
.peek(n -> System.out.printf("original: %d%n", n)) ➊
.map(n -> n * 2)
.peek(n -> System.out.printf("doubled : %d%n", n)) ➋
.filter(n -> n % 3 == 0)
.peek(n -> System.out.printf("filtered: %d%n", n)) ➌
.sum();
}
❶ 在倍增前打印值
❷ 在倍增后、筛选前打印值
❸ 在筛选后、求和前打印值
程序将显示每个元素的初始值、倍增值以及筛选后的值,输出结果如下:
original: 100
doubled : 200
original: 101
doubled : 202
original: 102
doubled : 204
filtered: 204
...
original: 119
doubled : 238
original: 120
doubled : 240
filtered: 240
略显不便的是,很难将用于测试的 peek
方法设置为可选。因此,尽管 peek
方法有助于调试,但不应将其置于生产环境中。