问题描述
我正在阅读Java 8,特别是Streams API。我想知道流是如何懒惰的?
I am reading Java 8, specifically the "Streams API". I wanted to know how streams can be lazy?
我相信流只是作为一个库添加,并且没有对语言进行任何更改来支持懒惰。另外,如果有人告诉我这是通过反思实现的,我会感到震惊。
I believe streams are just added as a library and there are no changes done to the language to support laziness. Also, I will be shocked if somebody tells me it's achieved through reflection.
推荐答案
为什么你需要反思才能得到懒惰?例如,考虑这个类:
Why would you need reflection to get laziness? For example, consider this class:
class LazySeq<T> {
private final List<T> list;
private Predicate<? super T> predicate;
public LazySeq(List<T> input) {
this.list = new ArrayList<>(input);
}
//Here you just store the predicate, but you don't perform a filtering
//You could also return a new LazySeq with a new state
public LazySeq<T> filter(Predicate<? super T> predicate) {
this.predicate = predicate;
return this;
}
public void forEach(Consumer<? super T> consumer){
if(predicate == null) {
list.forEach(consumer);
} else {
for(T elem : list) {
if(predicate.test(elem)) {
consumer.accept(elem);
}
}
}
}
}
当您在lazy seq上调用过滤器
时,过滤不会立即发生,例如:
When you call filter
on the lazy seq, the filtering does not happen immediately so for example:
LazySeq<Integer> lazySeq = new LazySeq<>(Arrays.asList(1, 2, 3, 4));
lazySeq = lazySeq.filter(i -> i%2 == 0);
如果在调用过滤器后看到序列的内容,你会发现它总是 1,2,3,4
。但是,当调用终端操作(例如 forEach
)时,将在使用消费者之前完成过滤。例如:
If you see the content of the sequence after calling filter, you'll see that it's always 1, 2, 3, 4
. However when calling a terminal operation, such as forEach
, then the filtering will be done before using the consumer. So for example:
lazySeq.filter(i -> i%2 == 0).forEach(System.out::println);
将打印2和4.
这与 Stream
s的原理相同。从源中,链接具有某些属性的操作。这些操作是中间的,它返回一个惰性流(例如过滤器
或 map
)或终端(例如的forEach
)。其中一些终端操作是短路的(例如 findFirst
),因此您可能无法遍历所有管道(您可以想到返回的for循环中的早期返回例如,数组中值的索引。
This is the same principle with Stream
s. From a source, you chain operations which have certains properties. These operations are either intermediate, which returns a lazy stream (such as filter
or map
), or terminal (such as forEach
). Some of these terminal operations are short-circuiting (such as findFirst
), so you might not traverse all the pipeline (you can think of an early return in a for loop that returns the index of a value in an array for example).
调用终端操作时,此操作链开始执行,以便最后得到预期的结果。
When calling a terminal operation, this chain of operations start to execute so that at the end you get the expected result.
懒惰可以通过在应用中间操作时在管道上存储新状态来实现,当你调用终端操作时,你可以通过所有状态 - 一对一的数据。
Laziness can be achieved by storing a new state on the pipeline when an intermediate op is applied, and when you call a terminal op, you go by all the states one-by-one on the data.
Stream API并没有真正实现(它有点复杂)但实际上原则就在这里。
The Stream API is not really implemented that way (it's a bit more complex) but really the principle is here.
这篇关于如何在Java 8中实现延迟流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!