请在下面考虑以下代码:
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”
在输出中也是..请帮助。
答案 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