java中间流操作未按计数计算
我似乎很难理解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());
}
控制台仅打印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());
}
输出更改为:
4
1234
这种看似冗余的过滤操作如何改变合成流管道的行为
# 1 楼答案
在我的JDK版本中,
count()
终端操作最终执行以下代码:如果在操作管道中有一个
filter()
操作,那么最初已知的流的大小就无法再知道了(因为filter
可能会拒绝流中的一些元素)。因此if
块不被执行,中间操作被执行,因此StringBuilder被修改另一方面,如果管道中只有
map()
,则流中的元素数保证与初始元素数相同。因此执行if块,直接返回大小,而不评估中间操作请注意,传递给
map()
的lambda违反了文档中定义的约定:它应该是一个无干扰、无状态的操作,但不是无状态的。因此,在这两种情况下产生不同的结果不能被视为错误# 2 楼答案
在jdk-9中,它被清晰地记录在java文档中
API注释:
# 3 楼答案
这不是什么。地图是给你的。它应该被用来把一个“某物”流变成一个“其他东西”流。在本例中,您使用map将一个字符串附加到外部Stringbuilder,之后将有一个“Stringbuilder”流,每个“Stringbuilder”流都是通过向原始Stringbuilder附加一个数字的map操作创建的
您的流实际上不会对流中的映射结果执行任何操作,因此完全有理由假设流处理器可以跳过该步骤。你指望副作用来完成这项工作,这打破了地图的功能模型。使用forEach做这件事会更好。将计数完全作为一个单独的流,或者在forEach中使用AtomicInt放置一个计数器
过滤器强制它运行流内容,因为它现在必须对每个流元素执行一些概念上有意义的操作