java流的底层原理(二)

Stream的结构

以下是我从jdk8中拷贝下来的代码

public interface Stream<T> extends BaseStream<T, Stream<T>> 

public interface BaseStream<T, S extends BaseStream<T, S>>
        extends AutoCloseable {

public interface AutoCloseable

通过以上代码我们可以看出,interface Stream 会继承接口BaseStream<T, Stream,而接口BaseStream<T, Stream是继承AutoCloseable。

Stream only be operated once

注意观察上面代码中的泛型特别有意思,因为Stream继承BaseStream<T, Stream>,而BaseStream<T, Stream>的两个泛型是T和
Stream,而在BaseStream中是这样定义BaseStream<T, S extends BaseStream<T, S>>及S应该是Stream类型的,而Stream类型有是实现BaseStream<T, Stream> 接口的,而BaseStream中定义的S也正好是S extends BaseStream<T, S>!

简单介绍一下BaseStream<T, S extends BaseStream<T, S>>中的方法:

1、Iterator iterator();返回一个迭代器
2、Spliterator spliterator();返回一个分割迭代器
3、boolean isParallel();判断是否为并行流
4、S sequential();生成一个串行流
5、S parallel();生成一个并行流
6、S unordered();返回一个无序的流,有可能返回 它本身。
7、S onClose(Runnable closeHandler);。这个方法会返回一个新的关闭处理器,当流被关闭之后此关闭处理器会被执行,并且执行顺序是按照被添加的顺序执行
比如:list.stream().onClose(R1).onClose(R2).
这段代码代表流被关闭之后会先执行R1然后执行R2.
举个例子:

public class BaseStreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","hello world");
       
        try(Stream<String> stringStream = list.stream()){
            stringStream.onClose(() -> {
                System.out.println("第一个关闭处理器执行");
                throw new NullPointerException("第一个处理器异常啦!");
                
            }).onClose(() -> {
                System.out.println("第二个关闭处理器执行");
                throw new NullPointerException("第二个关闭处理器异常啦!");
            }).forEach(System.out::println);
        }
    }

}

下面是上面的代码运行的结果

hello
world
hello world
第一个关闭处理器执行
第二个关闭处理器执行
Exception in thread "main" java.lang.NullPointerException: 第一个处理器异常啦!
	at com.bins9.MyBaseStream.BaseStreamTest.lambda$main$0(BaseStreamTest.java:15)
	at java.util.stream.Streams$1.run(Streams.java:850)
	at java.util.stream.AbstractPipeline.close(AbstractPipeline.java:323)
	at com.bins9.MyBaseStream.BaseStreamTest.main(BaseStreamTest.java:21)
	Suppressed: java.lang.NullPointerException: 第二个关闭处理器异常啦!
		at com.bins9.MyBaseStream.BaseStreamTest.lambda$main$1(BaseStreamTest.java:19)
		at java.util.stream.Streams$1.run(Streams.java:854)
		... 2 more

Process finished with exit code 1

结论
1、可以看到如果R1出现问题,并不会影响R2的执行。
2、如果R1和R2都抛出异常则将R1异常抛出,R2的异常是作为R1的压制异常出现的及Suppressed。

我们可以在研究一下如果这两个抛出同一个异常会怎么样?

public class BaseStreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","hello world");
        NullPointerException nullPointerException = new NullPointerException("异常啦");
        try(Stream<String> stringStream = list.stream()){
            stringStream.onClose(() -> {
                System.out.println("第一个关闭处理器执行");
//                throw new NullPointerException("第一个处理器异常啦!");
                throw nullPointerException;
            }).onClose(() -> {
                System.out.println("第二个关闭处理器执行");
//                throw new NullPointerException("第二个关闭处理器异常啦!");
                throw nullPointerException;
            }).forEach(System.out::println);
        }
    }

}


结果:
hello
world
hello world
第一个关闭处理器执行
第二个关闭处理器执行
Exception in thread "main" java.lang.NullPointerException: 异常啦
	at com.bins9.MyBaseStream.BaseStreamTest.main(BaseStreamTest.java:11)

Process finished with exit code 1

结果只有一个异常,因此如果R1和R2抛出同一个异常,则R1不会压制R2的异常,而是抛出同一个异常。

Stream的运行原理

通过一段代码我们来研究stream的底层运行原理:

	public static void main(String[] args) {
        List<String> list  = Arrays.asList("hello","world","bins");
        Set<String> set = list.stream().map(item -> item).forEach(System.out::println);
    }

我们可以跟着stream方法进去。
在Collection接口中我们找到如下代码

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

继续
在StreamSupport类中会有如下代码:

    public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head<>(spliterator,
                                            StreamOpFlag.fromCharacteristics(spliterator),
                                            parallel);
    }

stream的一次创建,最终会通过ReferencePipeline的Head方法构建。
ReferencePipeline中我们看到


        /**
         * Constructor for the source stage of a Stream.
         *
         * @param source {@code Spliterator} describing the stream source
         * @param sourceFlags the source flags for the stream source, described
         *                    in {@link StreamOpFlag}
         */
        Head(Spliterator<?> source,
             int sourceFlags, boolean parallel) {
            super(source, sourceFlags, parallel);
        }

Head方法是创建一个源流。

注意:default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}。方法的会传入方法spliterator()。

接下来我们看看spliterator()是做什么的。

 @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

	public static <T> Spliterator<T> spliterator(Collection<? extends T> c,
                                                 int characteristics) {
        return new IteratorSpliterator<>(Objects.requireNonNull(c),
                                         characteristics);
    }

这个方法通过Spliterators的spliterator创建一个Spliterator分割迭代器。创建的方式是通过new IteratorSpliterator<>(Objects.requireNonNull©,characteristics);。
继续跟进去

 public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {
            this.collection = collection;
            this.it = null;
            this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
                                   ? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
                                   : characteristics;
        }

咦!我们发现了集合赋值的地方,也就是说,流在创建源的时候就会将集合赋值给 Spliterator的 collection 变量(collection 是Spliterator中定义的常量!)

接下来我们分析中间操作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));
                    }
                };
            }
        };
    }

注意:这里是通过下游的Sink来执行上游的操作,也就是我们所有的操作是通过下游去找上游再找到源以此去执行!
重写的accept方法是真正执行的操作!
发现,map方法中会返回一个StatelessOp,阅读一下java文档

 /**
         * Construct a new Stream by appending a stateless intermediate
         * operation to an existing stream.
         *
         * @param upstream The upstream pipeline stage
         * @param inputShape The stream shape for the upstream pipeline stage
         * @param opFlags Operation flags for the new stage
         */
        StatelessOp(AbstractPipeline<?, E_IN, ?> upstream,
                    StreamShape inputShape,
                    int opFlags) {
            super(upstream, opFlags);
            assert upstream.getOutputShape() == inputShape;
        }

发现StatelessOp是代表中间无状态的中间操作。

跟进去构造方法!

/**
     * Constructor for appending an intermediate operation onto an existing
     * pipeline.
     *
     * @param upstream the upstream element source.
     */
    ReferencePipeline(AbstractPipeline<?, P_IN, ?> upstream, int opFlags) {
        super(upstream, opFlags);
    }

发现这个StatelessOp会将中间操作附加到已经存在的管道上,即,所有的流中间操作,都会统一到一个管道上一块执行!并不是一个循环执行一次操作!!!

这个方法中会传入一个AbstractPipeline,查看源码我们可以发现AbstractPipeline中提供了一些

 /**
     * Backlink to the head of the pipeline chain (self if this is the source
     * stage).
     */
    @SuppressWarnings("rawtypes")
    private final AbstractPipeline sourceStage;


 /**
     * The "upstream" pipeline, or null if this is the source stage.
     */
    @SuppressWarnings("rawtypes")
    private final AbstractPipeline previousStage;

  /**
     * The next stage in the pipeline, or null if this is the last stage.
     * Effectively final at the point of linking to the next pipeline.
     */
    @SuppressWarnings("rawtypes")
    private AbstractPipeline nextStage;

	
    /**
     * The number of intermediate operations between this pipeline object
     * and the stream source if sequential, or the previous stateful if parallel.
     * Valid at the point of pipeline preparation for evaluation.
     */
    private int depth;

depth:最终会通过depth来判断有没有要执行的操作!
等方法,可以发现这个AbstractPipeline类似于双向链表,可以通过它找到头缘,以及上游源,下游源!

而StatelessOp,和Head方法都在ReferencePipeline接口中,因此我们有必要阅读一下ReferencePipeline的介绍,

/**
 * Abstract base class for an intermediate pipeline stage or pipeline source
 * stage implementing whose elements are of type {@code U}.
 *
 * @param <P_IN> type of elements in the upstream source
 * @param <P_OUT> type of elements in produced by this stage
 *
 * @since 1.8
 */

发现ReferencePipeline是代表中间操作和管道源。

返回的StatelessOp实例中会重写opWrapSink方法,而此方法会返回一个Sink<P_OUT>,Sink又是什么呢?
阅读源码:

interface Sink<T> extends Consumer<T> 

Sink会继承Consumer,并阅读java doc

A {@code Sink} instance is used to represent each stage of this pipeline,
一个sink表示的是pipeline的每一个阶段,
它提供了三个方法:
Begin(初始化状态) accept(操作) end(激活状态)

也就是说流的每个中间操作会先初始化状态,然后进行操作,在终止状态,继续进行下一个操作!

通过collect终止操作,我们分析这个stream。

    @Override
    public void forEach(Consumer<? super P_OUT> action) {
        evaluate(ForEachOps.makeRef(action, false));
    }

通过:ForEachOps.makeRef(action, false)
我们发现了一个重要的接口TerminalOp<E_IN, R> ,这个代表终止操作,
它有一下几个工厂类:
FindOp 查找的
ForEachOp 遍历的
MatchOp 进行匹配的
ReduceOp 进行聚合的

这里面会提供串行和并行的操作,并行操作是引用jdk7提供的fork/join框架来实现的!

    public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,
                                                  boolean ordered) {
        Objects.requireNonNull(action);
        return new ForEachOp.OfRef<>(action, ordered);
    }

继续跟进去

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

我们看看串行流是怎么操作的!

        @Override
        public <S> Void evaluateSequential(PipelineHelper<T> helper,
                                           Spliterator<S> spliterator) {
            return helper.wrapAndCopyInto(this, spliterator).get();
        }


    @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,wrapSink是将输入流的操作执行并且将结果传递给Sink
而copyInto是将串联好的Sink应用到spliterator中所存放的collect,进行执行。

继续跟进去

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

发现了,此时会通过Spliterator来遍历wrappedSink,就将所有的中间操作和终止操作一一执行了。

简单总结一下以上类或者接口
中间操作
BaseStream 下是AbstractPipeline下是ReferencePiPElINE(中间操作)
ReferencePiPElINE下还有一个Head(头源)
ReferencePiPElINE下还有一个StateLessOp代表无状态的中间操作
ReferencePiPElINE下还有一个StateFulOp代表有状态的中间操作
终止操作
顶层是TerminalOp
它下面是
FindOp 查找的
ForEachOp 遍历的
MatchOp 进行匹配的
ReduceOp 进行聚合的

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值