我似乎无法理解 Java 如何将流操作组合到流管道中。
执行以下代码时
public
static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Run Code Online (Sandbox Code Playgroud)
控制台只打印4. 该StringBuilder对象仍然具有值""。
当我添加过滤操作时: filter(s -> true)
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.filter(s -> true)
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Run Code Online (Sandbox Code Playgroud)
输出更改为:
4
1234
Run Code Online (Sandbox Code Playgroud)
这个看似多余的过滤操作如何改变组合流管道的行为?
JB *_*zet 40
的count()终端操作,在我的版本的JDK,结束执行以下代码:
if (StreamOpFlag.SIZED.isKnown(helper.getStreamAndOpFlags()))
return spliterator.getExactSizeIfKnown();
return super.evaluateSequential(helper, spliterator);
Run Code Online (Sandbox Code Playgroud)
如果filter()操作管道中有操作,则最初已知的流的大小将无法再知道(因为filter可能会拒绝流的某些元素)。所以if不执行块,执行中间操作,从而修改 StringBuilder。
另一方面,如果您只有map()在管道中,则流中的元素数量保证与初始元素数量相同。所以执行了if块,直接返回size,不求中间操作。
请注意,传递给的 lambdamap()违反了文档中定义的契约:它应该是一个无干扰的、无状态的操作,但它不是无状态的。因此,在两种情况下都有不同的结果不能被视为错误。
Dea*_*ool 19
在jdk-9 中,它清楚地记录在 java docs 中
消除副作用也可能令人惊讶。除了 forEach 和 forEachOrdered 的终端操作之外,当流实现可以在不影响计算结果的情况下优化掉行为参数的执行时,行为参数的副作用可能并不总是被执行。(有关特定示例,请参阅有关计数操作的 API 说明。)
API注意事项:
如果能够直接从流源计算计数,则实现可以选择不执行流管道(顺序或并行)。在这种情况下,不会遍历源元素,也不会评估中间操作。强烈建议不要使用具有副作用的行为参数,除了调试等无害情况外,可能会受到影响。例如,考虑以下流:
List<String> l = Arrays.asList("A", "B", "C", "D");
long count = l.stream().peek(System.out::println).count();
Run Code Online (Sandbox Code Playgroud)
流源(一个 List)覆盖的元素数量是已知的,中间操作 peek 不会从流中注入或删除元素(对于 flatMap 或过滤器操作可能就是这种情况)。因此,计数是列表的大小,并且不需要执行管道,并且作为副作用,打印出列表元素。
| 归档时间: |
|
| 查看次数: |
1093 次 |
| 最近记录: |