流和懒惰的评估

时间:2014-01-19 16:53:12

标签: java java-8 java-stream

我正在阅读流抽象的java 8 API但是 我不太理解这句话:

  

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

当过滤操作创建新流时,该流是否包含已过滤的元素? 似乎可以理解,流只有在遍历时才包含元素,即终端操作。但是,包含过滤流的内容是什么?我糊涂了!!!

4 个答案:

答案 0 :(得分:48)

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

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;
}

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

答案 1 :(得分:12)

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

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

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

调用终端操作的时间,流的遍历开始,相关的功能逐个执行。

并行流不会逐个评估流(在终端点)。根据可用的核心,操作是同时执行的。

答案 2 :(得分:1)

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

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());
        }

Otput:

  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

看起来像流创建的迭代集数量,但是设置了一个新的流元素lazy。

与循环计数器比较:

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

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

答案 3 :(得分:-1)

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

让我们看看中间操作是多么懒惰吗? 我们有一个map()函数,在其中打印当前的学生姓名。仅当我们将终端操作符应用于这些名称时,才会打印这些名称。在下面的示例中,我们应用了collect(terminal operator),并且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():########

在地图声明之后

线程现在处于运行状态

在地图上:索拉卜 在地图上:罗伯特 在地图上:约翰 在地图上:罗马 在地图上:兰迪

##结束lazyIntermediateOperations()的执行

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

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