Evg*_*aev 30 java side-effects java-8 java-stream sonarqube
我在 Sonar 中发现了一条规则:
与其他中间 Stream 操作的一个关键区别是 Stream 实现可以自由地跳过调用以
peek()
达到优化目的。这可能会导致peek()
意外地仅调用 Stream 中的部分元素或不调用任何元素。
另外,Javadoc中也提到了这一点:
此方法的存在主要是为了支持调试,您希望在元素流经管道中的某个点时查看它们
什么情况下可以java.util.Stream.peek()
跳过?和调试有关系吗?
Nik*_*las 27
不但可以peek
,还map
可以跳过。这是为了优化。例如,当count()
调用终端操作时,单个项目没有意义peek
,因为map
此类操作不会改变当前项目的数量/计数。
下面是两个例子:
1.不会跳过Map 和 peek,因为过滤器可以预先更改项目数量。
long count = Stream.of("a", "aa")
.peek(s -> System.out.println("#1"))
.filter(s -> s.length() < 2)
.peek(s -> System.out.println("#2"))
.map(s -> {
System.out.println("#3");
return s.length();
})
.count();
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)#1 #2 #3 #1 1
2.由于项目数量不变,因此跳过Map 和 peek 。
long count = Stream.of("a", "aa")
.peek(s -> System.out.println("#1"))
//.filter(s -> s.length() < 2)
.peek(s -> System.out.println("#2"))
.map(s -> {
System.out.println("#3");
return s.length();
})
.count();
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)2
重要提示:这些方法应该没有副作用(它们如上所述,但仅用于示例)。
一般来说,不鼓励流操作的行为参数产生副作用,因为它们通常会导致无意中违反无状态性要求,以及其他线程安全隐患。
下面的实现是危险的。假设callRestApi
方法执行 REST 调用,则不会执行该调用,因为 Stream 违反了副作用。
long count = Stream.of("url1", "url2")
.map(string -> callRestApi(HttpMethod.POST, string))
.count();
Run Code Online (Sandbox Code Playgroud)
/**
* Performs a REST call
*/
public String callRestApi(HttpMethod httpMethod, String url);
Run Code Online (Sandbox Code Playgroud)
Ale*_*nko 13
peek()
是一个中间操作,它期望消费者对流的元素执行操作(副作用)。
如果流管道不包含可以更改流中元素数量的中间操作takeWhile
,例如、filter
、limit
等,并以终端操作 count()
结束,并且流源允许评估流中的元素数量然后count()
简单地询问源并返回结果。所有中间操作都得到优化。
注意:这种操作优化自Java 9count()
以来就存在(请参阅 API 注释),与没有直接关系,它会影响每个不改变流中元素数量的中间操作(目前这些是、、)。peek()
map()
sorted()
peek()
peek()
在其他中间业务中具有非常特殊的利基。
从本质上讲,它peek()
不同于其他中间操作,例如map()
以及导致副作用的终端操作(就像这样做),为到达它们的每个元素执行最终操作,即和。peek()
forEach()
forEachOrdered()
关键是它对流执行的结果peek()
没有影响。它永远不会影响终端操作产生的结果,无论是值还是最终操作。
也就是说,如果我们peek()
从管道中扔掉,并不会影响终端的运行。
该方法的文档peek()
以及Stream API 文档警告其操作可能会被省略,您不应该依赖它。
引用自以下文档peek()
:
如果流实现能够优化部分或全部元素的生成(例如使用 findFirst 等短路操作,或者在 count() 中描述的示例中),则不会为这些元素调用该操作。
引用 API 文档副作用段落:
副作用的消失也可能令人惊讶。除了终端操作
forEach
和之外forEachOrdered
,当流实现可以优化行为参数的执行而不影响计算结果时,行为参数的副作用可能并不总是被执行。
这是流的示例(链接到源),其中除了以下内容之外,没有任何中间操作被省略peek()
:
Stream.of(1, 2, 3)
.parallel()
.peek(System.out::println)
.skip(1)
.map(n -> n * 10)
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
因此,在此管道中,peek()
您skip()
可能希望它在控制台上显示源中的每个元素。但是,它不会发生(元素1
不会被打印)。由于peek()
其性质,可以在不破坏代码的情况下对其进行优化,即不影响终端操作。
这就是为什么文档明确指出此操作专门用于调试目的,并且不应为它分配在任何情况下都需要执行的操作。
归档时间: |
|
查看次数: |
4144 次 |
最近记录: |