繁体   English   中英

流和惰性求值

[英]Stream and lazy evaluation

我正在阅读有关流抽象的java 8 API ,但我不太明白这句话:

中间操作返回一个新的流。 他们总是很懒惰; 执行诸如 filter() 之类的中间操作实际上并不执行任何过滤,而是创建一个新流,该流在遍历时包含与给定谓词匹配的初始流的元素。 管道源的遍历直到管道的终端操作被执行后才开始。

当过滤操作创建新流时,该流是否包含过滤元素? 它似乎理解流仅在它被遍历时才包含元素,即使用终端操作。 但是,比,什么包含过滤后的流? 我糊涂了!!!

这意味着过滤器仅在终端操作期间应用。 想想这样的事情:

public Stream filter(Predicate p) {
    this.filter = p; // just store it, don't apply it yet
    return this; // in reality: return a new stream
}
public List collect() {
    for (Object o : stream) {
        if (filter.test(o)) list.add(o);
    }
    return list;
}

(那不编译,是对现实的简化,但原理在那里)

流是惰性的,因为除非调用终端操作,否则不会评估中间操作。

每个中间操作都会创建一个新流,存储提供的操作/函数并返回新流。

管道累积这些新创建的流。

调用终端操作的时候,开始遍历流,并一一执行相关的功能。

并行流不会“一一”评估流(在终点)。 这些操作是同时执行的,具体取决于可用的内核。

在我看来,中间操作并不完全是懒惰的:

List<String> l3 = new ArrayList<String>();
        l3.add("first");
        l3.add("second");
        l3.add("third");
        l3.add("fouth");
        l3.add("fith");
        l3.add("sixth");

        List<String> test3 = new ArrayList<String>();
        try {
            l3.stream().filter(s -> { l3.clear(); test3.add(s); return true;}).forEach(System.out::println);
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("!!! ");
            System.out.println(test3.stream().reduce((s1, s2) -> s1 += " ;" + s2).get());
        }

输出:

  first
    null
    null
    null
    null
    null
    java.util.ConcurrentModificationException
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1380)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
        at test.TestParallel.main(TestParallel.java:69)
    !!! 

    first ;null ;null ;null ;null ;null

看起来像流创建时的迭代集数,但获得了一个新的流元素懒惰。

与计数器循环比较:

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(); 
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    int i = 0;
    while (i < list.size()) {
        System.out.println(list.get(i++));
        list.clear();
    }
}

输出:

1

只有一次预期的迭代。 我同意流中异常抛出行为的问题,但我认为懒惰意味着只有当我要求某个对象去做时才获取数据(或执行某些操作); 并且数据的计数也是数据。

这意味着过滤器仅在终端操作期间应用。

让我们看看中间操作是如何懒惰的? 我们有一个map()函数,我们在其中打印当前的学生姓名。 只有在我们应用终端运营商时才会打印这些名称。 在下面的示例中,我们应用了collect(终端运算符),map()在线程进入运行状态后打印学生名称。 这就是中间操作的工作原理。

private static void lazyIntermediateOperations(List<Student> students) throws InterruptedException {
 System.out.println("######## Executing lazyIntermediateOperations() : ######## ");
 Stream<String> studentStream = students.stream()
            .map(student -> {
           System.out.printf("In Map : %s\n", student.getName());
           return student.getName().toUpperCase();
      });

 System.out.println("After map statement");
 Thread.sleep(5000);
 System.out.println("Thread is in Running state now");
 studentStream.collect(Collectors.toList());
 System.out.println("######## Ending the execution of lazyIntermediateOperations() ######## ");
}

输出继电器

##执行lazyIntermediateOperations():########

地图声明后

线程现在处于Running状态

在地图:Saurabh在地图:罗伯特在地图:约翰在地图:罗马在地图:兰迪

##结束lazyIntermediateOperations()的执行

有关更多信息,请参阅以下链接: https//onlyfullstack.blogspot.com/2018/12/intermediate-and-terminal-operations-in-java-8.html

http://onlyfullstack.blogspot.com/p/java-8-features.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM