创建Stream后更改Stream的源时Stream的行为

时间:2019-09-25 07:09:34

标签: java java-8 java-stream

请在下面考虑以下代码:

     List<Integer> l=new ArrayList<>();
     l.add(23);l.add(45);l.add(90);

     Stream<Integer> str=l.stream();     // mark A

     l.add(111);
     l=null;

     System.out.println(str.collect(Collectors.toList())); // mark B

输出是:

[23, 45, 90, 111]

我在这里假设当在标记B处调用终端操作时,将评估标记A的RHS 这意味着最近列表(带有元素“ 111”)已被选中,但问题是为什么我们没有得到 NullPointerException在这里。.如果我们没有收到异常,那么我们就不应该得到“ 111” 在输出中也是..请帮助。

2 个答案:

答案 0 :(得分:5)

此行为在the documentation中有明确说明:

  

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

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

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

答案 1 :(得分:1)

l.stream()创建一个Stream,它可能保留对源List的引用(我说这可能是因为这是实现细节)。

在消耗Stream时,List已经有4个元素,因此要消耗4个元素。

l引用更改为null不会影响存储在Stream实例中的引用。

  

如果我们没有得到应有的待遇,那么我们也不应该在输出中得到“ 111”。

仅当Stream实现创建源List的副本而不是仅保留对原始List的引用时,这才是正确的。由于这会浪费内存,因此这不是行为也就不足为奇了。

看看Collection的{​​{1}}的实现(至少在Java 8中如此),我看到:

stream()

default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } spliterator()覆盖:

ArrayList

public Spliterator<E> spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); } 保留对源ArrayListSpliterator的引用,如预期的那样:

List