Java 8 Streams中间操作会生成新的流吗?

4
当我在流上调用中间操作时,它们会为每个中间操作创建新的流吗?
List<Integer> list = List.of(1,2,3,4,5);

list.stream().filter(i -> i > 4).map(o -> o*2).collect(Collectors.toList());

它创建了几个流?是1个还是3个?

巧合的是或者有意为之,输入过滤器中过滤的元素数量也是三个。所以如果您能进一步澄清,您的问题是这样的 - “对于每个元素,都会有一个新的流吗?”还是您的问题是这样的 - “对于每个操作,例如filtermap,都会有一个新的流吗?” - Naman
谢谢,Naman。我已经得到了答案。 - Faraz
哦,好的@Naman。我知道你为什么会感到困惑了。我的问题清楚地说明了每个新的中间操作是否创建并返回一个新的流。这就是我的问题。答案也反映了这一点。 - Faraz
1
我编辑了我的问题,现在过滤函数只返回1个值。这个答案最为简洁。https://dev59.com/Ibnoa4cB1Zd3GeqPXctB#60677967。然而,由于缺乏详细信息,我没有接受它。 - Faraz
如果您使用Intellij Idea,它可以提示您每个操作返回的特定流。请检查“Inlay提示>方法链”选项。 - Yevgen
我每天都使用Eclipse @Eugene和Eclipse > Intellij(除了在这种情况下:) - Faraz
3个回答

3
基本上,函数式编程的一个概念是纯函数,它是确定性的,没有副作用,被称为不可变性。在Stream API中的所有中间操作都是不可变的,并返回新的Stream。
正如Oracle文档中提到的,所有中间操作(例如filter、map等)都返回新的流。例如,map方法的文档如下:
Stream<R> map(Function<? super T,? extends R> mapper)

返回一个流,其中包含将给定函数应用于此流的元素的结果。这是一种中间操作。

类型参数:

R - 新流的元素类型

参数:

mapper - 一个非干扰、无状态的函数,用于应用于每个元素。

返回:新的流

有关更多信息,您可以查看 Java 中的 Stream 实现(例如返回一个新的 StatelessOp 的 map 方法)。

public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
    Objects.requireNonNull(mapper);
    return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
                                 StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
        @Override
        Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
            return new Sink.ChainedReference<P_OUT, R>(sink) {
                @Override
                public void accept(P_OUT u) {
                    downstream.accept(mapper.apply(u));
                }
            };
        }
    };
}

2
是的,调用filter和map函数都会创建一个新的StatelessOp对象,这个对象是实现了Stream接口的新的ReferencePipeline对象。你将创建3个新的StatelessOp对象。

2

简而言之,是的。每次调用这样的方法都会创建另一个Stream对象。如果您拆分链并将这些中间操作分配给变量,可以看到这一点:

Stream<Integer> s1 = list.stream();
System.out.println(System.identityHashCode(s1));
Stream<Integer> s2 = s1.filter(i -> i > 2);
System.out.println(System.identityHashCode(s2));
Stream<Integer> s3 = s2.map(o -> o*2);
System.out.println(System.identityHashCode(s3));
List<Integer> result = s3.collect(Collectors.toList());
System.out.println(result);

样例输出:

1163157884
1747585824
1149319664
[6, 8, 10]


谢谢Mereinik。你能否回答我的附加问题? - Faraz
如果多个线程尝试将值插入非同步列表中,最终的列表会有差异吗?请注意,多个线程同时插入值,但在读取时,它们都返回到主方法。当所有插入完成后,才读取列表。 - Faraz
@Faraz 如果你有另一个无关的问题,请将其发布为问题,而不是评论。 - Mureinik
你能否回答这个问题吗:https://stackoverflow.com/q/60678142/4828463 - Faraz
感谢@Mureinik。我找到了一个类似的问题(List .add线程安全)适用于C#。我相信同样的事情也适用于Java。 - Faraz
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接