java8的stream流操作的数据结构

在java8的流操作中分为终止符和非终止符。

非终止符不会触发数据的处理。

每次通过stream()方法新建立一个流的时候都会创建一个Head类。这个类是ReferencePipeline的一个内部类,同时也继承了ReferencePipeline,代表当前流的一个初始状态。之后每进行一次中间操作,都会根据操作的类型生成一个StatelessOp或者StatefulOp代表有无状态的中间操作,这些都和Head一样继承自ReferencePipeline。而ReferencePipeline继承自AbstractPipeline,可以看到AbstractPipeline中一个重要的成员。

private final AbstractPipeline previousStage;

结合下面的流操作的map()方法可以看到。

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

每次调用一次中间操作,的确没有真的进行关于数据的确切操作,而是重新返回了一个StatelessOp,对于这个map()方法,重写了opWrapSink()方法,这个方法将在终止符操作中被调用。在创建这个中间无状态操作符的时候,参数需要传入当前的流操作,最后在其父类的构造方法中将会被赋值给previousStage,也就是说,流操作中的中间操作,实则是以前一步操作为参数创建了一个新的操作符,只要根据previousStage往前回溯直到一开始创建stream得到的Head类就是整个流操作的逆序,也就是说,在流操作的过程中遇到终止符之前,一直都是往链表之前创建新的操作符节点。

 

然而,仅仅建立这样一个链表显然是不够的,每个中间操作符都重写了opWrapSink()方法,在终止符操作具体的数据之间,将会从链表的头部一次调用链表中的操作符节点的opWrapSink()方法,进行具体的流操作包装。

在调用到类似forEach()的终止操作时候,将会正式对流进行包装,可以看到wrapSink()方法。

final  Sink wrapSink(Sink 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) sink;
}

在这个方法中,链表头部,也就是流操作的最后一个中间操作,将会不断调用其在一开始重写的opWrapSink()方法,对流进行包装。

以map()和limit()各自实现的opWrapSink()方法为例子。

@Override
Sink opWrapSink(int flags, Sink sink) {
    return new Sink.ChainedReference(sink) {
        @Override
        public void accept(P_OUT u) {
            downstream.accept(mapper.apply(u));
        }
    };
}

@Override
Sink opWrapSink(int flags, Sink sink) {
    return new Sink.ChainedReference(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();
        }
    };
}

可以看到,不管具体的操作如何,实则都是生成的都是一个ChainedReference类,这个类都将上一个包装完毕的类作为参数存放到自己的downstream属性中,这样,本身逆序的链表,将会从内到外进行包装,最后生成的包装类的最外层正式有逻辑正序上第一个操作符所产生的ChainedReference,而这个ChainedReference实现的accept()方法中,实现的正是具体的逻辑操作,并将本次操作的结果作为参数交给由它包装的downstream,也就是逻辑上下一个操作符所产生的ChainedReference调用accept()方法执行下一步逻辑操作,通过不断的包装达到流操作的目的。

你可能感兴趣的:(jdk)