java stream 原理

需求
从"Apple" "Bug" "ABC" "Dog"中选出以A开头的名字,然后从中选出最长的一个,并输出其长度

1. 最直白的实现

缺点
  1. 迭代次数过多
  2. 频繁产生中间结果,性能无法接受

2. 平常写法

int longest = 0;
for(String str : strings){
if(str.startsWith("A")){// 1. filter(), 保留以张开头的字符串
int len = str.length();// 2. mapToInt(), 转换成长度
longest = Math.max(len, longest);// 3. max(), 保留最长的长度
}
}
System.out.println(longest);
缺点
  1. 具体业务与算法混在一起,不利于代码复用
  2. 耦合性太强,代码不清晰

3. 责任链模式解耦

public interface Chain {
void proceed(Object object);
}
public class ForChain implements Chain {

    private final Chain chain;

    public ForChain(Chain chain){
this.chain = chain;
} @Override
public void proceed(Object object) {
List<String> list = (List<String>) object;
for(String a : list){
if(a.startsWith("A"))
chain.proceed(a);
}
}
}
public class LengthChain implements Chain {
private final Chain chain; public LengthChain(Chain chain){
this.chain = chain;
}
@Override
public void proceed(Object object) {
String string = (String)object;
chain.proceed(string.length());
}
}
public class ResultChain implements Chain {

    private Integer result = 0;
@Override
public void proceed(Object object) {
Integer integer = (Integer) object;
result = Math.max(integer,result);
} public Integer getResult() {
return result;
}
}
public class Client {

    public static void main(String[] args) {
ResultChain resultChain = new ResultChain();
LengthChain lengthChain = new LengthChain(resultChain);
ForChain forChain = new ForChain(lengthChain);
List<String> list = Arrays.asList("Apple","Bug","ABC","Dog");
forChain.proceed(list);
System.out.println("result is "+ resultChain.getResult());
}
}

4. java stream 实现

OptionalInt max = Stream.of("Apple", "Bug", "ABC", "Dog").
filter(e -> e.startsWith("A")).
mapToInt(e -> e.length()).
max();
System.out.println("result is "+ max.getAsInt());
优点
  1. 开发者是需要关注具体的业务,顶层算法都封装在框架中
  2. 代码结构清晰,代码量少,减少出错的机会

5. Stream 的原理

5.1 stream与集合比较

尽管stream与集合框架在表现上非常相似,二者都是对数据进行处理,但事实上二者完全不同。集合是一种数据结构,主要关注在内存中组织数据,会在一段时间在内存中持续的存在,而流的主要关注在计算,不为数据提供任何存储空间,只会通过管道提供计算结果。

5.2 stream 操作分类

中间操作:返回一个新的stream

  • 有状态:必须等上一步操作完,才能执行下一步操作
  • 无状态:该操作不受上一步操作的影响

终止操作:返回结果

  • 短路:找到即返回
  • 费短路:遍历所有元素

以上操作决定了Stream一定是先构建完毕再执行的特点,也就是延迟执行,当需要结果(终端操作时)开始执行流水线。

5.3 stream 结构示意图

5.4 操作如何记录
  • Head记录起始操作
  • StateLessOp记录中间操作
  • StatefulOp记录有状态的中间操作

这三个操作,在实例化的时候回指向前一个操作,和后一个操作,形成双向链表,每一步操作都能得知上一步和下一步操作。

对于Head:

AbstractPipeline(Spliterator<?> source,
int sourceFlags, boolean parallel) {
this.previousStage = null;
this.sourceSpliterator = source;
this.sourceStage = this;
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
// The following is an optimization of:
// StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
this.depth = 0;
this.parallel = parallel;
}

对于其他操作:

AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if (previousStage.linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
previousStage.nextStage = this; // 构造双向链表
this.previousStage = previousStage; this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
this.sourceStage = previousStage.sourceStage;
if (opIsStateful())
sourceStage.sourceAnyStateful = true;
this.depth = previousStage.depth + 1;
}

例子:

data.stream()
.filter(x -> x.length() == 2)
.map(x -> x.replace(“三”,”五”))
.sorted()
.filter(x -> x.contains(“五”))
.forEach(System.out::println);

Stage

5.5 操作如何叠加

从终止操作依次构造Sink,如此Sink链构造完成

final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink); // 依次构造sink
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;
}

sink

  1. 依次调用sink的begin方法,通知sink链数据已准备好
  2. 依次调用sink的accept方法,处理数据
  3. 依次调用sink的end方法,通知数据处理完毕
@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);
}
}

5.6 如何收集结果

对于forEach是不需要收集结果的,对于collect结果保存在最后一个sink中,这样的操作都会提供一个get方法取出数据。终止操作都会实现Supplier的get方法

@Override
public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
Spliterator<P_IN> spliterator) {
return helper.wrapAndCopyInto(makeSink(), spliterator).get();
}
public interface Supplier<T> {

    /**
* Gets a result.
*
* @return a result
*/
T get();
}
interface TerminalSink<T, R> extends Sink<T>, Supplier<R> { }

java stream 原理的更多相关文章

  1. [JavaEE]Java NIO原理图文分析及代码实现

    转http://weixiaolu.iteye.com/blog/1479656 目录: 一.java NIO 和阻塞I/O的区别      1. 阻塞I/O通信模型      2. java NIO ...

  2. Java NIO原理分析

    Java IO 在Client/Server模型中,Server往往需要同时处理大量来自Client的访问请求,因此Server端需采用支持高并发访问的架构.一种简单而又直接的解决方案是“one-th ...

  3. Java NIO原理图文分析及代码实现

    原文: http://weixiaolu.iteye.com/blog/1479656 目录: 一.java NIO 和阻塞I/O的区别      1. 阻塞I/O通信模型      2. java ...

  4. Java NIO原理 图文分析及代码实现

    Java NIO原理图文分析及代码实现 前言:  最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请 ...

  5. [源码解析] 当 Java Stream 遇见 Flink

    [源码解析] 当 Java Stream 遇见 Flink 目录 [源码解析] 当 Java Stream 遇见 Flink 0x00 摘要 0x01 领域 1.1 Flink 1.2 Java St ...

  6. Java Stream 源码分析

    前言 Java 8 的 Stream 使得代码更加简洁易懂,本篇文章深入分析 Java Stream 的工作原理,并探讨 Steam 的性能问题. Java 8 集合中的 Stream 相当于高级版的 ...

  7. Java Stream 自定义Collector

    Collector的使用 使用Java Stream流操作数据时,经常会用到各种Collector收集器来进行数据收集. 这里便深入了解一点去了解Collector的工作原理和如何自定义Collect ...

  8. JAVA监听器原理

    http://blog.csdn.net/longyulu/article/details/25054697 JAVA监听器原理 标签: 监听器 2014-05-05 15:40 9070人阅读 评论 ...

  9. Java跨平台原理

    此篇博文主要源自网络xiaozhen的天空的博客:http://xiaozhen1900.blog.163.com/blog/static/1741732572011325111945246/ 1.是 ...

随机推荐

  1. 在Editplus中配置java的(带包)编译(javac)和运行(java)的方法

    配置的前提是电脑安装了JDK并且配置好了相关的环境变量(JAVA_HOME,path和classpath). 配置好后在命令行中输入javac和java验证是否配置成功: 如果出现上面的情况则说明配置 ...

  2. Numpy库(个人学习笔记)

    一样,咱的计算机还是得先拥有Python,并且安装了Numpy库.有疑问的话可以看这里呦~~~~ 下面开讲: NumPy的主要对象是齐次多维数组.它是一个元素表(通常是数字),并且都是相同类型,由正整 ...

  3. 数据库入门之运行原始 SQL 查找

    数据库入门之运行原始 SQL 查找 一旦你设置好了数据库连接,就可以使用 DB facade 来进行查找.DB facade 提供每个类型的查找方法:select.update.insert.dele ...

  4. Idea中开启assert断言

    先打开相应的配置面板,有以下两种方式.   然后在VM栏里输入 -enableassertions 或者 -ea 就好了 然后编写程序试试 我的目录结构如下:(因为Main class那里要写类的全限 ...

  5. 用JNDI连接数据库

    之前说到了利用Java中的Properties类读取properties配置文件,连接数据库,现在说另一种方法,他们的目的和作用都是一样的,都是为了提高代码的复用性,解决了更改数据库 时还要更改代码的 ...

  6. Vue解析五之mounted

    在mounted获得vue的实例要加 this.$nextTick(function () { // 代码保证 this.$el 在 document 中 }) mounted: function ( ...

  7. 希尔排序(shell‘ sort)

    希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进.希尔排序又叫缩小增量排序 基本思想: 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录 ...

  8. 【Python】 sys和os模块

    sys sys模块能使程序访问于python解释器联系紧密的变量和函数 ● sys中的一些函数和变量 argv 命令行参数构成的列表 path 查找所有可用模块所在的目录名的列表 platform 查 ...

  9. Java基础学习笔记二十五 MySQL

    MySQL 在dos中操作mysql 连接mysql命令: mysql -uroot -p密码 ,连接OK,会出现mysql> 对数据库的操作 创建一个库 create database 库名 ...

  10. 假设检验(Hypothesis Testing)

    假设检验(Hypothesis Testing) 1. 什么是假设检验呢? 假设检验又称为统计假设检验,是数理统计中根据一定假设条件由样本推断总体的一种方法. 什么意思呢,举个生活中的例子:买橘子(借 ...