考虑在创建过滤流后添加的列表元素

时间:2018-12-22 13:47:54

标签: java java-8 java-stream

给出以下代码:

List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));

Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));

strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation

System.out.println(Arrays.toString(jFilter.toArray())); 

输出:

[Java, JavaScript, JQuery]

即使JavaScriptJQuery在创建过滤后的流之后添加,为什么仍会出现在过滤结果中?

6 个答案:

答案 0 :(得分:13)

简短答案

您假设在这一点之后:

Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));

返回以“ J”开头的新元素流,即仅返回Java。但是,不是情况;

流是惰性的,即它们不执行任何逻辑,除非终端操作另行通知。

流管道的实际执行从toArray()调用开始,由于列表在终端toArray()操作开始之前被修改,因此结果为[Java, JavaScript, JQuery]

更长的答案

这是documentation的一部分,其中提到了这一点:

  

对于行为良好的流源,可以先修改源   终端操作开始,这些修改将是   反映在覆盖的元素中。例如,考虑以下   代码:

 List<String> l = new ArrayList(Arrays.asList("one", "two"));
 Stream<String> sl = l.stream();
 l.add("three");
 String s = sl.collect(joining(" "));  
     

首先创建一个列表,该列表由两个字符串组成:“一个”;和“两个”。然后创建一个流   从该列表中。接下来,通过添加第三个字符串来修改列表:   “三”。最后,流的元素被收集并加入   一起。由于列表是在终端收集之前被修改的   操作开始后,结果将为字符串“一二三”。   从JDK集合以及大多数其他JDK返回的所有流   类,以这种方式表现良好;

答案 1 :(得分:8)

直到语句

System.out.println(Arrays.toString(jFilter.toArray()));

运行,流不执行任何操作。要遍历流并执行中间操作(在这种情况下为toArray,则需要执行终端操作(在示例中为filter)。

在这种情况下,您可以做的是,例如,在添加其他元素之前捕获列表的大小:

int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
                                  .filter(str -> str.startsWith("J"));

其中limit(maxSize)不允许超过初始元素的流水线。

答案 2 :(得分:3)

这是因为流从未得到评估。您从来没有在该流上调用“ 终端操作”,因为它们懒惰会被执行。

查看对代码和输出的修改。过滤实际上是在您呼叫终端操作员时进行的。

Lines <- "
date    firms return
5/1/1988    A   5
6/1/1988    A   6
7/1/1988    A   4
8/1/1988    A   5
9/1/1988    A   6
11/1/1988   A   6
12/1/1988   A   13
13/01/1988  A   3
14/01/1988  A   2
15/01/1988  A   5
16/01/1988  A   2
18/01/1988  A   7
19/01/1988  A   3
20/01/1988  A   5
21/01/1988  A   7
22/01/1988  A   5
23/01/1988  A   9
25/01/1988  A   1
26/01/1988  A   5
27/01/1988  A   2
28/01/1988  A   7
29/01/1988  A   2
5/1/1988    B   5
6/1/1988    B   7
7/1/1988    B   5
8/1/1988    B   9
9/1/1988    B   1
11/1/1988   B   5
12/1/1988   B   2
13/01/1988  B   7
14/01/1988  B   2
15/01/1988  B   5
16/01/1988  B   6
18/01/1988  B   8
19/01/1988  B   5
20/01/1988  B   4
21/01/1988  B   3
22/01/1988  B   18
23/01/1988  B   5
25/01/1988  B   2
26/01/1988  B   7
27/01/1988  B   3
28/01/1988  B   9
29/01/1988  B   2
"
DF <- read.table(text = Lines, header = TRUE)

输出:

  // Set up the models
    var User = sequelize.define('User', {});
    var Login = sequelize.define('Login', {});
        ...


    User.create({
       name: "name",
       Login:
            {
            users: {..i.e several users if a user belongs to another user..}
            }
        },{
       include:{
      model: Login,
      include: User //nested model.Create
         }
    })

答案 3 :(得分:1)

如官方文档,https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html所述,流没有存储空间,因此更像迭代器而不是集合,并且被懒惰地评估。

因此,在调用终端操作toArray()之前,流实际上不会发生任何事情

答案 4 :(得分:1)

@Hadi J的评论,但应该按照规则回答。

  

因为streams很懒,当您调用终端操作时,它会执行。

答案 5 :(得分:0)

toArray方法是终端操作,适用于列表的全部内容。为了获得可预测的结果,请不要将stream保存到临时变量中,因为这会导致误导结果。更好的代码是:

String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();