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

mmu*_*hid 20 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())); 
Run Code Online (Sandbox Code Playgroud)

哪个输出:

[Java, JavaScript, JQuery]
Run Code Online (Sandbox Code Playgroud)

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

Ous*_* D. 13

简答

你在这一点之后假设:

Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));
Run Code Online (Sandbox Code Playgroud)

返回以"J"开头的元素的新流,即仅返回Java.然而事实并非如此;

流是懒惰的,即除非终端操作另有说明,否则它们不执行任何逻辑.

流管道的实际执行在toArray()调用时开始, 并且由于在终端toArray()操作开始之前修改了列表,结果将是[Java, JavaScript, JQuery].

更长的答案

这里提到的文档的一部分:

对于性能良好的流源,可以在终端操作开始之前修改源,并且这些修改将反映在所覆盖的元素中.例如,请考虑以下代码:

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

首先创建一个包含两个字符串的列表:"one"; 和"两个".然后从该列表创建流.接下来,通过添加第三个字符串来修改列表:"three".最后,收集流的元素并将它们连接在一起.由于在终端收集操作开始之前修改了列表,结果将是一串"一二三".从JDK集合和大多数其他JDK类返回的所有流都以这种方式表现良好;


ern*_*t_k 8

直到声明

System.out.println(Arrays.toString(jFilter.toArray()));
Run Code Online (Sandbox Code Playgroud)

运行,流不做任何事情.toArray要遍历的流和执行中间操作(filter在这种情况下)需要终端操作(在示例中).

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

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

哪里limit(maxSize)不允许超过初始元素通过管道.