java流的中间操作源码解析

本文详细解析了Java Stream API的工作原理,重点介绍了中间操作与终端操作的区别,通过具体实例展示了filter和map操作的内部实现机制,揭示了Stream如何通过链表式的Pipeline结构高效处理数据。

Stream的操作分为中间操作和终端操作。

中间操作: 由于stream的延迟性,中间操作相当于“我打算要进行这个操作,但是现在先不急着操作。”例如filter,sort,limit等。

终端操作:执行流的所有中间操作并且关闭流,使得流无法被复用。

中间操作

中间操作是对流中元素的一种操作,这个操作可以化为一个函数,实际上源码也是这样设计的。因此这里我直接举filter操作为例子。

首先我们看在interface Stream中对filter的定义.

Stream<T> filter(Predicate<? super T> predicate);
复制代码

返回由与此给定谓词匹配的此流的元素组成的流。

显然传入的是一个函数。

这时候往下看它的实现:

@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
    Objects.requireNonNull(predicate);
    return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                 StreamOpFlag.NOT_SIZED) {
        @Override
        Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
            return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
                @Override
                public void begin(long size) {
                    downstream.begin(-1);
                }

                @Override
                public void accept(P_OUT u) {
                    if (predicate.test(u))
                        downstream.accept(u);
                }
            };
        }
    };
}
复制代码

我们可以看到,实现中使用了new,目前我认为在这个实现中并没有对源流的数据进行操作或者说它可能另外复制了一份流进行返回(虽然第二种可能性很低,因为效率的开销太大)。

然后再看opWrapSink方法,该方法使用了传入的函数

@Override
public void accept(P_OUT u) {
    if (predicate.test(u))
        downstream.accept(u);
}
复制代码

根据我做数据过滤的经历downstream.accept(u);操作应该是一个添加数据的操作。点进去查看之后发现他也是个函数

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    /** andThen */
}
复制代码

而ChainedReference这个类只是Sink接口中部分实现Sink的抽象类。而Sink继承了Consumer函数接口

而更上一层返回的StatelessOp类,他也是一个继承了ReferencePipeline并部分实现的抽象类。这里我们看到,StatelessOp是一个ReferencePipeline类,而ReferencePipeline类我在最早的StreamSupport的stream中已经知道了是一个原始流的类。也就是说它是一个新的流。那么它和旧的流之间是什么关系呢?

我们直接看最底层的构造方法的赋值

AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
    if (previousStage.linkedOrConsumed)
        throw new IllegalStateException(MSG_STREAM_LINKED);
    previousStage.linkedOrConsumed = true;
    previousStage.nextStage = this;

    this.previousStage = previousStage;
    this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
    this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
    this.sourceStage = previousStage.sourceStage;
    if (opIsStateful())
        sourceStage.sourceAnyStateful = true;
    this.depth = previousStage.depth + 1;
}
复制代码

其中previousStage是旧的流,源码中称它为“上游管道”。然后我们可以看到

previousStage.nextStage = this;
复制代码

这行代码,到了这里既视感就很强烈了,这不就是链表吗?!那么根据源码我们可以知道stream中表示数据的Pipeline和中间操作新创建的Pipeline是链表关系,那么其他操作是不是一样呢?这里我快速查看了map操作:

@Override
@SuppressWarnings("unchecked")
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));
                }
            };
        }
    };
}
复制代码

返回的依旧是StatelessOp对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值