常用的stream方法
中间操作 | 无状态 | unordered、filter、map、mapToInt、mapToLong、mapToDouble、flatMap、flatMapToInt、flatMapToLong、flatMapToDouble、peek |
中间操作 | 有状态 | distinct、sorted、limit、skip |
终止操作 | 非短路操作 | forEach、forEachOrdered、toArray、reduce、collect、max、min、count |
终止操作 | 短路操作 | anyMatch、allMatch、noneMatch、findFirst、findAny |
有无状态指的是元素的处理受不受之前元素的影响。
短路和非短路指的是遇到符合条件的元素就返回。
需要解决的问题
- 如何记录每次操作
- 操作如何叠加
- 叠加后的操作如何执行
- 最后结果如何存储
stream包的分类
- 主要是各种操作的工厂类、数据的存储结构以及收集器的工厂类等;
- 主要用于Stream的惰性求值实现;
- Stream的并行计算框架;
- 存储并行流的中间结果;
- 终结操作的定义
解决问题
如何记录每次操作
使用stage标记每一次的操作,而stream又需要一个callback,因此完整的操作是由<DataSource、Ops、Callback>三元组表示,具体实现时,使用实例化的ReferencePipeline来表示,如
@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));
}
};
}
};
}
如何叠加
stage记录了每个操作,但是没有执行的逻辑,因此定义了Sink接口,如下:
interface Sink<T> extends Consumer<T> {
/**
* Resets the sink state to receive a fresh data set. This must be called
* before sending any data to the sink. After calling {@link #end()},
* you may call this method to reset the sink for another calculation.
* @param size The exact size of the data to be pushed downstream, if
* known or {@code -1} if unknown or infinite.
*
* <p>Prior to this call, the sink must be in the initial state, and after
* this call it is in the active state.
*/
default void begin(long size) {}
/**
* Indicates that all elements have been pushed. If the {@code Sink} is
* stateful, it should send any stored state downstream at this time, and
* should clear any accumulated state (and associated resources).
*
* <p>Prior to this call, the sink must be in the active state, and after
* this call it is returned to the initial state.
*/
default void end() {}
/**
* Indicates that this {@code Sink} does not wish to receive any more data.
*
* @implSpec The default implementation always returns false.
*
* @return true if cancellation is requested
*/
default boolean cancellationRequested() {
return false;
}
/**
* Accepts an int value.
*
* @implSpec The default implementation throws IllegalStateException.
*
* @throws IllegalStateException if this sink does not accept int values
*/
default void accept(int value) {
throw new IllegalStateException("called wrong accept method");
}
}
如何执行
调用Sink内的方法组合执行:
@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);
}
}
如何存储
Sink下的Consumer、Node、SpinedBuffer
- 对于返回boolean或者Optional的操作的操作,由于值返回一个值,只需要在对应的Sink中记录这个值,等到执行结束时返回就可以了
- 对于collect(), reduce(), max(), min()归约操作,存放在用户调用时指定的容器中
- 对于返回是数组的情况,存储在一种叫做Node的数据结构中的。
并行执行
stream的并行执行是基于ForkJoin框架来实现的。
影响并行执行的因素:
- 数据大小:数据够大,每个管道处理时间够长,并行才有意义;
- 源数据结构:每个管道操作都是基于初始数据源,通常是集合,将不同的集合数据源分割会有一定消耗;
- 装箱:处理基本类型比装箱类型要快;
- 核的数量:默认情况下,核数量越多,底层fork/join线程池启动线程就越多;
- 单元处理开销:花在流中每个元素身上的时间越长,并行操作带来的性能提升越明显;
源数据结构分类:
- 性能好:ArrayList、数组或IntStream.range(数据支持随机读取,能轻易地被任意分割)
- 性能一般:HashSet、TreeSet(数据不易公平地分解,大部分也是可以的)
- 性能差:LinkedList(需要遍历链表,难以对半分解)、Stream.iterate和BufferedReader.lines(长度未知,难以分解)
Work Stealing原理:
- 每个工作线程都有自己的工作队列WorkQueue,这是一个双端队列dequeue,它是线程私有的;
- ForkJoinTask中fork的子任务,将放入运行该任务的工作线程的队头,工作线程将以LIFO的顺序来处理工作队列中的任务,即堆栈的方式;
- 为了最大化地利用CPU,空闲的线程将从其它线程的队列中「窃取」任务来执行,但是是从工作队列的尾部窃取任务,以减少和队列所属线程之间的竞争;
- 双端队列的操作:push()/pop()仅在其所有者工作线程中调用,poll()是由其它线程窃取任务时调用的;
- 当只剩下最后一个任务时,还是会存在竞争,是通过CAS来实现的;
java8Stream原理深度解析
https://www.cnblogs.com/Dorae/p/7779246.html