整个 sort() 是否先发生然后传递给 filter() ?
那么这不是违反了流应该做的事情吗?
不,不是。看看Stream IPA 的文档 https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/stream/package-summary.html#StreamOps:
中间操作又分为无状态和无状态
有状态操作。无状态操作, 例如filter and map,
不保留任何状态 from 先前见过的元素当处理一个新的
element——每个元素可以独立于操作进行处理
在其他元素上。有状态操作, 例如distinct and sorted,
may 合并国家 from 以前见过的元素 when 加工
新元素.
有状态操作可能需要process the 之前的整个输入
产生结果。例如,无法产生任何结果
对流进行排序,直到看到流的所有元素。作为一个
结果,在并行计算下,一些管道包含有状态
中间操作可能需要多次传递数据,或者可能
需要缓冲重要数据。管道仅包含
无状态中间操作可以一次性处理,
无论是顺序还是并行,都具有最少的数据缓冲。
这意味着sorted
知道所有先前遇到的元素,即stateful. But map
and filter
不需要这些信息,他们是无国籍的 and lazy,这些操作总是一次处理一个来自流源的元素。
从技术上讲,不可能通过单独查看单个元素来对管道的内容进行排序。sorted
运行于all“一次”元素并将排序后的流分发给下一个操作。你可能会想到sorted
就好像它成为了新的溪流源头。
让我们看一下下面的流并分析它将如何处理:
Stream.of("foo", "bar", "Alice", "Bob", "Carol")
.filter(str -> !str.contains("r")) // lazy processing
.peek(System.out::println)
.map(String::toUpperCase) // lazy processing
.peek(System.out::println)
.sorted() // <--- all data is being dumped into memory
.peek(System.out::println)
.filter(str -> str.length() > 3) // lazy processing
.peek(System.out::println)
.findFirst(); // <--- the terminal operation
运营filter
and map
之前的sorted
只会在需要时才延迟地应用于流源中的每个元素。 IE。filter将应用于"foo"
,成功通过了filter并被转化为map手术。然后filter被应用在"bar"
并且它不会到达map。那么就会是"Alice"
轮到通过filter,然后是map将在该字符串上执行。等等。
请记住sorted()
需要所有数据来完成其工作,因此第一过滤器将对源中的所有元素执行map将应用于通过过滤器的每个元素。
Then sorted()
操作会将流的所有内容转储到内存中,并对通过第一个过滤器的元素进行排序。
并且之后sorting,再次处理所有元素懒惰地一次一个。因此,第二个过滤器将被应用只有一次(虽然3
元素已通过第一个过滤器并已排序)。"Alice"
将通过第二个过滤器并到达终端操作findFirst()
这将返回这个字符串。
看一下调试输出peek()
使执行过程如上所述发生。