初步了解 java 8 stream 原理

1、为什么使用 Stream

for 循环或者 Iterator 循环的不足

  • 如果想要在一次循环迭代中完成多次操作(例如,在一次循环中,进行去重、过滤、元素转换、排序操作),会比较繁琐,甚至需要多次循环才能达到自己的目标。造成的不好的结果,一个是代码比较繁琐,一个是多次循环迭代会有性能损失(局部变量会增加,增加了内存的开支)
  • 如果希望使用并行处理,需要自己编写代码
  • 如果需要进行一些统计操作,需要自己编写相关代码,而这些代码往往又是比较繁杂的

Stream 的优势

  • 一次迭代中支持多种操作
  • 使用 Fork/Join 框架,支持了并行处理
  • 提供了常见统计场景的解决方案,且支持定义自己的统计方案,十分灵活(内部提供了一套简单的 mapReduce 机制X)
  • 支持函数式编程,代码风格更加简洁

2、一个简单的 Stream 栗子(后续源码分析也是根据这个例子进行)

需求是,获取一个 list 中大于 10 的前两名数字:

public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>();
        nums.add(3);
        nums.add(13);
        nums.add(33);
        nums.add(23);

        List<Integer> res = nums.stream()
                .filter(n->n>10)
                .sorted()
                .limit(2)
                .collect(
                        ArrayList::new,
                        ArrayList::add,
                        ArrayList::addAll
                );

        System.out.println(res.toString());
    }

flunt 风格的代码,看起来很舒服。

3、Stream 使用

一般,我们使用 Stream 的时候,是以下流程:

  1. 构建 Stream
  2. 中间操作1
  3. 中间操作2
  4. 中间操作…
  5. 终结操作

Stream 常用的流操作包括:

  • 中间操作(Intermediate Operations)

    • 无状态(Stateless)操作:每个数据的处理是独立的,不会影响或依赖之前的数据。如 filter()、flatMap()、flatMapToDouble()、flatMapToInt()、flatMapToLong()、map()、mapToDouble()、mapToInt()、mapToLong()、peek()、unordered() 等

    • 有状态(Stateful)操作:处理时会记录状态,比如处理了几个。后面元素的处理会依赖前面记录的状态,或者拿到所有元素才能继续下去。如 distinct()、sorted()、sorted(comparator)、limit()、skip() 等

  • 终止操作(Terminal Operations)

    • 非短路操作:处理完所有数据才能得到结果。如 collect()、count()、forEach()、forEachOrdered()、max()、min()、reduce()、toArray()等。
    • 短路(short-circuiting)操作:拿到符合预期的结果就会停下来,不一定会处理完所有数据。如 anyMatch()、allMatch()、noneMatch()、findFirst()、findAny() 等。

4、Stream 结构

Stream 操作存储结构,以及相关接口和类

Stream 的操作(主要指流的构建、中间操作)是以双向链表的形式存储的,核心类是 AbstractPipeline。为了方便后续的学习,我们可以简单认为,一个 AbstractPipeline 代表流的创建操作或者是一个中间操作。Stream 中创建流和中间操作会形成一个 AbstractPipeline 双向链表,每添加一个中间操作,就会在链表结尾新增一个节点。

在这里插入图片描述

在这里插入图片描述

几个核心接口:

  • BaseStream接口:主要定义了获取迭代器、支持分割的迭代器、并行流的转换、流的关闭能力
  • Stream 接口:定义了常见的中间操作和终结操作,也是 API 类

几个核心的抽象类:

  • PipelineHelper:定义了 sink 链表的构建、以及中间操作、终结操作的执行等能力
  • AbstractPipeline:最核心的一个类,构建了一个「中间操作和创建流操作」的双向列表,同时,继承了 PipelineHelper,也拥有 sink 链表的构建、以及操作的执行等能力
  • ReferencePipeline:继承了 AbstractPipeline,同时实现了 Stream 接口,提供了常见的中间操作、终结操作的能力

「操作」流水化调用相关的接口—sink

在这里插入图片描述
一个 sink 中封装了一个「操作」的执行过程。
在这里插入图片描述
Sink 接口的核心方法:

  • accept():进行迭代时,会将元素推入这个方法,真正进行操作
  • begin(): accept 执行之前调用的方法
  • end(): accept 执行之后调用的方法

Sink 的通用实现 ChainedReference 中会记录下游的 sink 指针,所以其实整个stream 操作中 sink 也是一个链式结构(其实是装饰器模式)。

在这里插入图片描述
中间操作的 sink 的创建由 AbstractPipeline#opWrapSink 创建,终结操作的 sink 由 .ReduceOps.ReduceOp#makeSink 创建,sink 链的建立,由 AbstractPipeline#wrapSink 完成

终结操作相关的接口

TreminalOp 是终结操作的顶级接口,定义了并行、串行执行中间操作的能力
在这里插入图片描述
有四个类,ForEachOp、MatchOp、ReduceOp、FindOp 实现了该接口,所以终结操作总体可以归纳到这 4 类中

5、Stream 源码解析

5.1、构建Stream 源

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

主要有两步

  • 一、构建一个 spliterator,我们可以认为一个 spliterator 是一个拥有分割元素能力的 Iterator。为了减少篇幅,这块直接略过。
  • 二、 使用 StreamSupport#stream(java.util.Spliterator, boolean) 构建一个 AbstractPipeline 对象。看下源码:
    public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head<>(spliterator,
                                            StreamOpFlag.fromCharacteristics(spliterator),
                                            parallel);
    }

最终是返回了一个 ReferencePipeline.Head 对象。看下这个类的结构:

    static class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> {
     
        Head(Supplier<? extends Spliterator<?>> source,
             int sourceFlags, boolean parallel) {
            super(source, sourceFlags, parallel);
        }

        Head(Spliterator<?> source,
             int sourceFlags, boolean parallel) {
            super(source, sourceFlags, parallel);
        }

        @Override
        final boolean opIsStateful() {
            throw new UnsupportedOperationException();
        }

		// 直接抛出了异常? 因为这个操作不需要被 warpSink
        @Override
        final Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink) {
            throw new UnsupportedOperationException();
        }
        @Override
        public void forEach(Consumer<? super E_OUT> action) {
            if (!isParallel()) {
                sourceStageSpliterator().forEachRemaining(action);
            }
            else {
                super.forEach(action);
            }
        }

        @Override
        public void forEachOrdered(Consumer<? super E_OUT> action) {
            if (!isParallel()) {
                sourceStageSpliterator().forEachRemaining(action);
            }
            else {
                super.forEachOrdered(action);
            }
        }
    }

这个类,存在的主要意义就是提供 spliterator,提供元素的迭代能力,以及作为「操作链」的第一个节点。

5.2、添加中间操作(着重看 sink 的实现逻辑)

5.2.1、添加 filter 操作

ReferencePipeline#filter

    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);
                    }
                };
            }
        };
    }

最终返回一个 StatelessOp 对象,也标志的这个操作是个无状态的中间操作。重点关注下 accept 方法,这个方法会直接调用下游 sink 的 accept 方法。

5.2.2、添加 sorted 操作

最终返回一个 SortedOps.OfRef 对象。看下这个类的 opWrapSink 方法:

         @Override
        public Sink<T> opWrapSink(int flags, Sink<T> sink) {
            Objects.requireNonNull(sink);

            if (StreamOpFlag.SORTED.isKnown(flags) && isNaturalSort)
                return sink;
            else if (StreamOpFlag.SIZED.isKnown(flags))
                return new SizedRefSortingSink<>(sink, comparator);
            else
                return new RefSortingSink<>(sink, comparator);
        }

看下 RefSortingSink 的实现:

    private static final class RefSortingSink<T> extends AbstractRefSortingSink<T> {
        private ArrayList<T> list;

        RefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) {
            super(sink, comparator);
        }

        @Override
        public void begin(long size) {
        // 会初始化一个 list
            if (size >= Nodes.MAX_ARRAY_SIZE)
                throw new IllegalArgumentException(Nodes.BAD_SIZE);
            list = (size >= 0) ? new ArrayList<T>((int) size) : new ArrayList<T>();
        }

        @Override
        public void end() {
        	// 排序
            list.sort(comparator);
            // 调用下游 sink 的 begin 方法
            downstream.begin(list.size());
            if (!cancellationWasRequested) {
            	// 调用下游 sink 的accept 方法
                list.forEach(downstream::accept);
            }
            else {
                for (T t : list) {
                    if (downstream.cancellationRequested()) break;
                    downstream.accept(t);
                }
            }
            // 调用下游的 end 方法
            downstream.end();
            list = null;
        }

        @Override
        public void accept(T t) {
        // 将元素加入 list
            list.add(t);
        }
    }

为什么不在 accept 中调用下游的方法呢?sorted() 是一个有状态的操作,一般会有一个属于自己的容器,用来记录处自己理过的数据的状态。sorted() 是在执行 begin 的时候初始化这个容器,在执行 accept 的时候把数据放到容器中,最后在执行 end 方法时才正在开始排序。排序之后再将数据,采用同样的方式依次传递给下游节点。

5.2.3、添加 limit 操作

            Sink<T> opWrapSink(int flags, Sink<T> sink) {
                return new Sink.ChainedReference<T, T>(sink) {
                    long n = skip;
                    long m = limit >= 0 ? limit : Long.MAX_VALUE;

                    @Override
                    public void begin(long size) {
                        downstream.begin(calcSize(size, skip, m));
                    }

                    @Override
                    public void accept(T t) {
                        if (n == 0) {
                            if (m > 0) {
                                m--;
                                downstream.accept(t);
                            }
                        }
                        else {
                            n--;
                        }
                    }

                    @Override
                    public boolean cancellationRequested() {
                        return m == 0 || downstream.cancellationRequested();
                    }
                };
            }

accept 方法中,只会选取前 m 个元素,然后继续调用下游 sink 的 accept 方法,上游通过 cancellationRequested 方法的返回值,来判断是否还需要将更多的元素 push 到本 sink 的 accept 方法中。

5.3、添加终结操作(wrapSink、执行操作[包含中间操作、终结操作])

collect 方法:

    public final <R> R collect(Supplier<R> supplier,
                               BiConsumer<R, ? super P_OUT> accumulator,
                               BiConsumer<R, R> combiner) {
        return evaluate(ReduceOps.makeRef(supplier, accumulator, combiner));
    }

首先看下 ReduceOps.makeRef(supplier, accumulator, combiner) 做了什么:

    public static <T, R> TerminalOp<T, R> makeRef(Supplier<R> seedFactory, BiConsumer<R, ? super T> accumulator, BiConsumer<R,R> reducer) {
        Objects.requireNonNull(seedFactory);
        Objects.requireNonNull(accumulator);
        Objects.requireNonNull(reducer);
        class ReducingSink extends Box<R>
                implements AccumulatingSink<T, R, ReducingSink> {
            @Override
            public void begin(long size) {
                state = seedFactory.get();
            }

            @Override
            public void accept(T t) {
            	// 将元素推入累加器
                accumulator.accept(state, t);
            }

            @Override
            public void combine(ReducingSink other) {
                reducer.accept(state, other.state);
            }
        }
        return new ReduceOp<T, R, ReducingSink>(StreamShape.REFERENCE) {
            @Override
            public ReducingSink makeSink() {
                return new ReducingSink();
            }
        };
    }

返回了一个 ReduceOp 对象,ReduceOp 实现了 TerminalOp 的 makeSink 方法,返回了 makeRef 方法中定义的 ReducingSink 类型的对象。
继续看下 evaluate 方法的逻辑:

    final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
        assert getOutputShape() == terminalOp.inputShape();
        if (linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        linkedOrConsumed = true;

        return isParallel()
               ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
               : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
    }

我们的例子中是串行,所以直接看下 terminalOp.evaluateSequential:

        public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
                                           Spliterator<P_IN> spliterator) {
            return helper.wrapAndCopyInto(makeSink(), spliterator).get();
        }

直接调用了 AbstractPipeline 中最后一个中间操作(即 limit 操作生成的 AbstractPipeline)的 wrapAndCopyInto 方法。这个方法也是分两步

  • makeSink() 创建终结操作对应的 sink
  • wrapAndCopyInto() warp 中间操作的 sink,并执行 sink

先看下,makeSink() 方法,就是我们上面创建的 ReduceOp 中实现的 makeSink 方法:

 @Override
            public ReducingSink makeSink() {
                return new ReducingSink();
            }

直接返回了 ReducingSink ,这个类,上面已经看过,就不赘述了,直接看下 wrapAndCopyInto 方法在拿到终结操作的 sink 后,做了什么:

    @Override
    final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
        copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
        return sink;
    }

也是分两步

  • wrapSink 方法,根据 AbstractPipeline 链表,进行中间操作的 sink 链的构建。
  • copyInto 方法则是真正执行中间操作和终结操作

wrapSink 方法:

    final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
        Objects.requireNonNull(sink);

        for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
            sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
        }
        return (Sink<P_IN>) sink;
    }

wrapSink 方法逻辑并不复杂,就是从 AbstractPipeline 链表最后一个节点开始循环(limit 节点),一直到第一个中间操作的节点(filter 节点),循环调用各个 AbstractPipeline 的 opWrapSink 方法,最终得到一个 sink,这个 sink 包含了所有中间操作和终结操作的逻辑。

接下来,看下 copyInto 方法的逻辑

    final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
        Objects.requireNonNull(wrappedSink);

        if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
            wrappedSink.begin(spliterator.getExactSizeIfKnown());
            spliterator.forEachRemaining(wrappedSink);
            wrappedSink.end();
        }
        else {
            copyIntoWithCancel(wrappedSink, spliterator);
        }
    }

从第一个中间操作对应的 sink 的 begin 方法开始执行,然后在迭代中将元素推入第一个 sink,执行 sink 链,最后执行第一个 sink 的 end 方法。

sink 装饰器对象创建的过程:

在这里插入图片描述

sink 装饰器对象执行的过程如下图:

在这里插入图片描述

6、总结

整个过程可以简单理解为:
构造 AbstractPipeline ==> 使用代理模式构造 Sink 对象 ==> 通过迭代器执行 sink 获取结果

所以,要理解 Stream 的原理,总体只需要理解一下几个过程:

  • AbstractPipeline 链的构建
  • AbstractPipeline ofWrapSink 的实现逻辑,因为 AbstractPipeline 中间操作的逻辑,都在 sink 里面

几个简单的问题:

  • 从接口设计上,sink 的存在意义是什么

引用

  • https://toutiao.io/posts/imbyvb/preview
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值