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 进行聚合的