Stream的深入(三)

心得:之前学习流,深入了流的底层。但是学的这些东西在平时日常开发的过程中,是根本不会用到的。只是为了更好帮助自己去理解流的底层设施。用起来也更自信,能够确定用的东西非常正确。

专注技术:这种纯技术的这种环境。

而不是说:专注业务开发了5年,技术没有长进。

这位张龙老师给讲课的方式,就是学习一门新技术的过程。如果觉得这种方式学习起来很有效的话。可以使用这种方式去学习一门新的技术。

lambda表达式和匿名内部类完全不同

之前虽然学了流了,但是还不太够。我们还缺少了一个能够把一个流从头到尾的执行过程给用起来的过程。

接下来会完成这个目的

用程序入门。

public class LambdaTest {
//内部类和lambda表达式到底有什么关系
Runnable r1 = () -> System.out.println(this); //匿名内部类 - 标识我生成了一个Runnable的实例 . 是一个类
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println(this);
}
}; public static void main(String[] args) {
LambdaTest lambdaTest = new LambdaTest();
Thread t1 = new Thread(lambdaTest.r1);
t1.start(); System.out.println("-------------");
Thread t2 = new Thread(lambdaTest.r2);
t2.start();
//请问,输出结果一样吗?
}
}

运行结果:

-------------
com.dawa.jdk8.LambdaTest@59a30351 (lambda表达式的结果)
com.dawa.jdk8.LambdaTest$1@2831008d (匿名内部类的结果) Process finished with exit code 0

LambdaTest$1, 这个1就是匿名内部类的类名。 (匿名内部类的名字)

经过对比,虽然说 lambda是匿名内部类的不同实现,但是 他们两个是完全一样的。原理不同。

结论:

  1. 匿名内部类会开辟一个新的作用域
  2. lambda是不会开辟新的作用域的

这里普及这个知识点,是为了以后在Debug的时候会发现 匿名内部类和lambda表达式的类名不同、


系统的去走一遍stream的执行流程

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

map()实现

返回值为StatelessOp

@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));
}
};
}
};
}
//StatelessOp类的定义和构造方法
/**
* Base class for a stateless intermediate stage of a Stream.
*
* @param <E_IN> type of elements in the upstream source
* @param <E_OUT> type of elements in produced by this stage
* @since 1.8
*/
abstract static class StatelessOp<E_IN, E_OUT>
extends ReferencePipeline<E_IN, E_OUT> {
/**
* 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;
} @Override
final boolean opIsStateful() {
return false;
}
}

StatelessOp继承ReferencePipeline,而ReferencePipeline实现了Stream.

所以map方法返回new StatelessOp<P_OUT, R>就等于返回了一个Stream

返回的是继承了StatelessOp的子类的对象。完成了上游和下游流的互通.

Reference Pipeline 无非就是一个双向链表

操作包装:map()方法中的 opWrapSink()的ChainedReference,实现了流的包装操作。把剩下的流给warp到一起

然后就一个元素,同时经过了剩下的方法操作。

@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 类
* <p>A sink may be in one of two states: an initial state and an active state.
* It starts out in the initial state; the {@code begin()} method transitions
* it to the active state, and the {@code end()} method transitions it back into
* the initial state, where it can be re-used. Data-accepting methods (such as
* {@code accept()} are only valid in the active state.
*

ChainedReference()链接引用

    /**
* Abstract {@code Sink} implementation for creating chains of
* sinks. The {@code begin}, {@code end}, and
* {@code cancellationRequested} methods are wired to chain to the
* downstream {@code Sink}. This implementation takes a downstream
* {@code Sink} of unknown input shape and produces a {@code Sink<T>}. The
* implementation of the {@code accept()} method must call the correct
* {@code accept()} method on the downstream {@code Sink}.
*/
static abstract class ChainedReference<T, E_OUT> implements Sink<T> {
protected final Sink<? super E_OUT> downstream; public ChainedReference(Sink<? super E_OUT> downstream) {
this.downstream = Objects.requireNonNull(downstream);
} @Override
public void begin(long size) {
downstream.begin(size);
} @Override
public void end() {
downstream.end();
} @Override
public boolean cancellationRequested() {
return downstream.cancellationRequested();
}
}

Sink类中的end(),和 begin()方法,切换两种状态:1.初始状态 2.激活状态

每一次accept()方法执行之前,需要调用Sink中的begin()方法,进入激活状态,执行完毕之后调用end()方法,进入初始状态。

涉及设计模式:模板方法模式。

opWrapSink() 的上级实现:

接受了一个Sink对象,这个对象接受了操作的结果,并且返回了一个Sink,还会执行这个操作,并将这个结果传递给所提供的sink。 *(输入参数才是带结果的sinK)

正是因为这种操作,才能将sink给包装起来。

    /**
* Accepts a {@code Sink} which will receive the results of this operation,
* and return a {@code Sink} which accepts elements of the input type of
* this operation and which performs the operation, passing the results to
* the provided {@code Sink}.
接受了一个Sink对象,这个对象接受了操作的结果,并且返回了一个Sink,还会执行这个操作,并将这个结果传递给所提供的sink。 *(输入参数才是带结果的sinK)
正式因为这种操作,才能将sink给包装起来。
*
* @apiNote
* The implementation may use the {@code flags} parameter to optimize the
* sink wrapping. For example, if the input is already {@code DISTINCT},
* the implementation for the {@code Stream#distinct()} method could just
* return the sink it was passed.
*
* @param flags The combined stream and operation flags up to, but not
* including, this operation * @param sink sink to which elements should be sent after processing
* @return a sink which accepts elements, perform the operation upon
* each element, and passes the results (if any) to the provided
* {@code Sink}.
参数本身是用来接收结果的,而不是用返回值来返回结果的。
*/
abstract Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink);
        @Override
final Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink) {
throw new UnsupportedOperationException();
}

流的特性:惰性求值和延迟求值。

map()方法,包括其他的peek(),filter()等等中间操作的这些方法。只是完成了返回了一个StatelessOp对象。

所以中间操作返回一个终止对象可能执行的StatelessOp,没有终止操作,所以流不会被处理。

那么终止操作。我们要去追一下了。


拿代码中写的 forEach()方法开始去追

    // Terminal operations from Stream

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

这是调用了makeRef()方法.方法在ForEachOps类中.

先看ForEachOps类的javadoc

/**
* Factory for creating instances of {@code TerminalOp} that perform an
* action for every element of a stream. Supported variants include unordered
* traversal (elements are provided to the {@code Consumer} as soon as they are
* available), and ordered traversal (elements are provided to the
* {@code Consumer} in encounter order.)
这是一个工厂,用来创建 TerminalOp 对象,(终止操作。)这个对象会对每一个元素执行一个动作。
所支持的变化包括:无序的遍历,有序的遍历(按照所提供的的顺序来遍历)。
*
* <p>Elements are provided to the {@code Consumer} on whatever thread and
* whatever order they become available. For ordered traversals, it is
* guaranteed that processing an element <em>happens-before</em> processing
* subsequent elements in the encounter order.
元素被提供被一个任何可用的Consumer队形。
处理一个元素,一定是发生在 另外一件事之前 (happens-before)。
也就事 先遇到的元素先处理,后遇到的元素后处理。 *
* <p>Exceptions occurring as a result of sending an element to the
* {@code Consumer} will be relayed to the caller and traversal will be
* prematurely terminated.
提供了大量的 静态方法。
*
* @since 1.8
*/
final class ForEachOps { }

如makeRef()

   /**
* Constructs a {@code TerminalOp} that perform an action for every element
* of a stream.
*
* @param action the {@code Consumer} that receives all elements of a
* stream
* @param ordered whether an ordered traversal is requested
* @param <T> the type of the stream elements
* @return the {@code TerminalOp} instance
*/
public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,
boolean ordered) {
Objects.requireNonNull(action);
return new ForEachOp.OfRef<>(action, ordered);
}

TerminalOp说明

默认执行的是串行的。

/**
* An operation in a stream pipeline that takes a stream as input and produces
* a result or side-effect. A {@code TerminalOp} has an input type and stream
* shape, and a result type. A {@code TerminalOp} also has a set of
* <em>operation flags</em> that describes how the operation processes elements
* of the stream (such as short-circuiting or respecting encounter order; see
* {@link StreamOpFlag}).
流管道中的一个操作。会接受一个流作为输入, 产生的结果,是有副作用的(副作用:你传递了一个引用,你修改了这个引用)。
一个 TerminalOp 会有一个输入类型,和流的shape 和一个结果类型。
TerminalOp 还会有一个 如何处理流中的元素 的标识。
TerminalOp 必须要提供一种 串行的和并行的 实现。 *
* <p>A {@code TerminalOp} must provide a sequential and parallel implementation
* of the operation relative to a given stream source and set of intermediate
* operations.
*
* @param <E_IN> the type of input elements
* @param <R> the type of the result
* @since 1.8
*/
interface TerminalOp<E_IN, R> {
/**
* Gets the shape of the input type of this operation.
*
* @implSpec The default returns {@code StreamShape.REFERENCE}.
*
* @return StreamShape of the input type of this operation
*/
default StreamShape inputShape() { return StreamShape.REFERENCE; } /**
* Gets the stream flags of the operation. Terminal operations may set a
* limited subset of the stream flags defined in {@link StreamOpFlag}, and
* these flags are combined with the previously combined stream and
* intermediate operation flags for the pipeline.
*
* @implSpec The default implementation returns zero.
*
* @return the stream flags for this operation
* @see StreamOpFlag
*/
default int getOpFlags() { return 0; } /**
* Performs a parallel evaluation of the operation using the specified
* {@code PipelineHelper}, which describes the upstream intermediate
* operations.
*
* @implSpec The default performs a sequential evaluation of the operation
* using the specified {@code PipelineHelper}.
*
* @param helper the pipeline helper
* @param spliterator the source spliterator
* @return the result of the evaluation
*/
default <P_IN> R evaluateParallel(PipelineHelper<E_IN> helper,
Spliterator<P_IN> spliterator) {
if (Tripwire.ENABLED)
Tripwire.trip(getClass(), "{0} triggering TerminalOp.evaluateParallel serial default");
return evaluateSequential(helper, spliterator);
} /**
* Performs a sequential evaluation of the operation using the specified
* {@code PipelineHelper}, which describes the upstream intermediate
* operations.
*
* @param helper the pipeline helper
* @param spliterator the source spliterator
* @return the result of the evaluation
*/
<P_IN> R evaluateSequential(PipelineHelper<E_IN> helper,
Spliterator<P_IN> spliterator);
}

终止操作的实现就4类:

1.find

2.match

3.forEach 遍历

4.reduce

返回去: forEach()操作就是返回了一个终止操作对象。

然后:evaluate()方法, 执行那个终止操作对象。


// Terminal evaluation methods /**
* Evaluate the pipeline with a terminal operation to produce a result.
*
* @param <R> the type of result
* @param terminalOp the terminal operation to be applied to the pipeline.
* @return the result
*/
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()));
}

PipelineHelper类

用来描述流的各种信息。

/**
* Helper class for executing <a href="package-summary.html#StreamOps">
* stream pipelines</a>, capturing all of the information about a stream
* pipeline (output shape, intermediate operations, stream flags, parallelism,
* etc) in one place.
Helper是一个帮助类,用于执行流管道
包含流管道的所有信息:源数据。输出类型,操作,流标识,并行标记等。 *
* <p>
* A {@code PipelineHelper} describes the initial segment of a stream pipeline,
* including its source, intermediate operations, and may additionally
* incorporate information about the terminal (or stateful) operation which
* follows the last intermediate operation described by this
* {@code PipelineHelper}. The {@code PipelineHelper} is passed to the
* {@link TerminalOp#evaluateParallel(PipelineHelper, java.util.Spliterator)},
* {@link TerminalOp#evaluateSequential(PipelineHelper, java.util.Spliterator)},
* and {@link AbstractPipeline#opEvaluateParallel(PipelineHelper, java.util.Spliterator,
* java.util.function.IntFunction)}, methods, which can use the
* {@code PipelineHelper} to access information about the pipeline such as
* head shape, stream flags, and size, and use the helper methods
* such as {@link #wrapAndCopyInto(Sink, Spliterator)},
* {@link #copyInto(Sink, Spliterator)}, and {@link #wrapSink(Sink)} to execute
* pipeline operations..
一个流管道的最初的分块,包含源,中间操作和增加的操作。等 PipelineHelper会被传递给。。。。 方法, 就可以通过PipelineHelper来访问管道的各种信息。 *
* @param <P_OUT> type of output elements from the pipeline
* @since 1.8
*/
abstract class PipelineHelper<P_OUT> {
...
}

PipelineHelper类里的方法:wrapAndCopyInto()

   /**
* Applies the pipeline stages described by this {@code PipelineHelper} to
* the provided {@code Spliterator} and send the results to the provided
* {@code Sink}.
将调用了这个方法的pipeline所描述的管道的各个阶段,同时 应用到Spliterator和发送给Sink对象
*
* @implSpec
* The implementation behaves as if:
* <pre>{@code
* intoWrapped(wrapSink(sink), spliterator);
* }</pre>
*
* @param sink the {@code Sink} to receive the results
* @param spliterator the spliterator describing the source input to process
*/
abstract<P_IN, S extends Sink<P_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator);

wrapAndCopyInto具体实现:

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

Sink中的wrapSink()方法

    /**
* Takes a {@code Sink} that accepts elements of the output type of the
* {@code PipelineHelper}, and wrap it with a {@code Sink} that accepts
* elements of the input type and implements all the intermediate operations
* described by this {@code PipelineHelper}, delivering the result into the
* provided {@code Sink}.
接受了一个Sink, Sink接受了PipelineHelper的所有输出类型。 *
* @param sink the {@code Sink} to receive the results
* @return a {@code Sink} that implements the pipeline stages and sends
* results to the provided {@code Sink}
*/
abstract<P_IN> Sink<P_IN> wrapSink(Sink<P_OUT> sink);

wrapSink()方法具体实现 (完成了对于多个流操作的串联。)

    @Override
@SuppressWarnings("unchecked")
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink); //根据depth判断是否有中间操作。 从后往前的去走。
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()


自我总结:Stream的执行流程。

源数据-中间操作-中间操作-终止操作

1.串联起来所有的操作。(中间操作 和 终止操作)

2.让流中的元素,一个一个的执行所含有的所有操作。

最核心的方法:copyInto()中的:spliterator.forEachRemaining(wrappedSink); //最最核心的一步

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

wrappedSink : 所有的中间操作,封装到了这个 sink对象

spliterator:源数据- 执行forEachRemaining 遍历,执行每一次这过sink对象封装的操作。

上面是静态分析(通过源码分析)

自行通过动态分析(程序Debug分析)


t通过Debug去跟一遍代码。

public class StreamTest3 {
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world", "welcome");
// list.stream().map(item->item+"_abc").forEach(System.out::println); Stream<String> stream = list.stream();
System.out.println("1");//断点
Stream<String> stream1 = stream.map(item -> item + "_abc");
System.out.println("2");//断点
stream1.forEach(System.out::println);
}
}

JAVA8学习——Stream底层的实现三(学习过程)的更多相关文章

  1. JAVA8学习——Stream底层的实现(学习过程)

    Stream底层的实现 Stream接口实现了 BaseStream 接口,我们先来看看BaseStream的定义 BaseStream BaseStream是所有流的父类接口. 对JavaDoc做一 ...

  2. JAVA8学习——Stream底层的实现一(学习过程)

    Stream底层的实现 Stream接口实现了 BaseStream 接口,我们先来看看BaseStream的定义 BaseStream BaseStream是所有流的父类接口. 对JavaDoc做一 ...

  3. JAVA8学习——Stream底层的实现二(学习过程)

    继续深入Stream的底层实现过程 2.spliterator() 接上 https://www.cnblogs.com/bigbaby/p/12159495.html 我们这次回到最开始源码分析的地 ...

  4. JAVA8学习——Stream底层的实现四(学习过程)

    Stream的深入(四) 从更高角度去看一下:类与类之间的设计关系 (借助IDEA的图形处理工具 Ctrl+Alt+U). ReferencePipeline的三个实现的子类: Head Statel ...

  5. JAVA8学习——深入浅出函数式接口FunctionInterface(学习过程)

    函数式接口 函数式接口详解:FunctionInterface接口 话不多说,先打开源码,查阅一番.寻得FunctionInterface接口 package java.util.function; ...

  6. Java8学习笔记(二)--三个预定义函数接口

    三个函数接口概述 JDK预定义了很多函数接口以避免用户重复定义.最典型的是Function: @FunctionalInterface public interface Function<T, ...

  7. Java8学习笔记目录

    Java8学习笔记(一)--Lambda表达式 Java8学习笔记(二)--三个预定义函数接口 Java8学习笔记(三)--方法引入 Java8学习笔记(四)--接口增强 Java8学习笔记(五)-- ...

  8. JAVA8学习——从使用角度深入Stream流(学习过程)

    Stream 流 初识Stream流 简单认识一下Stream:Stream类中的官方介绍: /** * A sequence of elements supporting sequential an ...

  9. JAVA8学习——深入浅出Lambda表达式(学习过程)

    JAVA8学习--深入浅出Lambda表达式(学习过程) lambda表达式: 我们为什么要用lambda表达式 在JAVA中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法. 在 ...

随机推荐

  1. jQuery对html元素的取值与赋值实例详解

    jQuery对html元素的取值与赋值实例详解 转载  2015-12-18   作者:欢欢   我要评论 这篇文章主要介绍了jQuery对html元素的取值与赋值,较为详细的分析了jQuery针对常 ...

  2. vue实现购物车逻辑

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Python3:ImportError: No module named 'compiler.ast'

    from compiler.ast import flatten 上面这条语句好像在python3 以后就废除了,如果使用的话就会报错.Traceback (most recent call last ...

  4. H3C ISDN与OSI参考模型

  5. uni-app学习记录03-路由跳转

    <template> <view class="content"> <!-- v-show是相对于display: none --> <v ...

  6. 浅谈集合框架三、Map常用方法及常用工具类

    最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...

  7. 第一种方式:cookie的优化与购物车实例

    一 Cookie 的优化 1.1 一般而言,我们设置cookie是在php中设置 例如: <?php setcookie('testKey1','hello world',0,'/'); //# ...

  8. Java开发之快捷键

    1.显示桌面快捷键:win+D或者右击状态栏,选择显示桌面. 2.UE编辑器:如果想把多行记录合并为一行,使用替换(Ctrl+R),查找里输入^p(代表回车换行符),替换为里什么都不填,替换位置选择所 ...

  9. LINUX内核参数调优集锦

    1.linux内核参数注释 2.两种修改内核参数方法 3.内核优化参数生产配置 1.linux内核参数注释 以下表格中红色字体为常用优化参数 根据参数文件所处目录不同而进行分表整理 下列文件所在目录: ...

  10. webpack4.0基本配置,超简单!

    最近复习了一下webpack,使用的是4.0版本. 下图是基本目录结构,最后留有代码地址,有兴趣可以去看看. 直接上代码(依赖未完全使用): 项目的所有依赖都可以安装,每个都有详细的注释.] cons ...