Java 8 中的惰性流是如何实现的? [英] How are lazy streams implemented in Java 8?

查看:26
本文介绍了Java 8 中的惰性流是如何实现的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读 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);
                }
            }
        }
    }
}

当你在惰性序列上调用 filter 时,过滤不会立即发生,例如:

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 的原理相同.从源头上,您链接具有某些属性的操作.这些操作要么是中间的,它返回一个惰性流(例如 filtermap),要么是终端的(例如 forEach).其中一些终端操作是短路的(例如 findFirst),因此您可能不会遍历所有管道(您可以考虑在 for 循环中提前返回,该循环返回值的索引例如一个数组).

This is the same principle with Streams. 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 中的惰性流是如何实现的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆