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;}@Overridepublic 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;}@Overridepublic void proceed(Object object) {String string = (String)object;chain.proceed(string.length());}
}
public class ResultChain implements Chain {private Integer result = 0;@Overridepublic 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);// 依次构造sinkfor ( @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> { }

转载于:https://www.cnblogs.com/dragonfei/p/8710240.html

java stream 原理相关推荐

  1. JAVA Stream原理和常见操作

    1.Stream原理 Stream是JAVA8开始的新的用法.Stream本意为流,Stream也确实如流一样,给操作带来了方便. Stream的表现是: 1)提供一个特定类型的队列: 2)提供一系列 ...

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

    最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.可以参考: ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 前言:  最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它 ...

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

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

  5. 《Java虚拟机原理图解》5. JVM类加载器机制与类加载过程

    参考网址:http://blog.csdn.net/luanlouis/article/details/50529868 0.前言 读完本文,你将了解到: 一.为什么说Jabalpur语言是跨平台的 ...

  6. 【Java 虚拟机原理】Class 字节码二进制文件分析 七 ( 局部变量表分析 )

    文章目录 前言 一.编译生成带局部变量表的字节码文件 二.局部变量表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | i ...

  7. 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )

    文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...

  8. 【Java 虚拟机原理】Class 字节码二进制文件分析 五 ( 方法计数器 | 方法表 | 访问标志 | 方法名称索引 | 方法返回值类型 | 方法属性数量 | 方法属性表 )

    文章目录 前言 一.方法表结构 二.方法计数器 三.方法表数据解析 ( init 构造方法 ) 1.方法访问标志 2.方法名称索引 3.方法返回类型 4.方法属性数量 前言 上一篇博客 [Java 虚 ...

  9. 【Java 虚拟机原理】Class 字节码二进制文件分析 四 ( 字段表数据结构 | 字段表详细分析 | 访问标志 | 字段名称 | 字段描述符 | 属性项目 )

    文章目录 前言 一.字段表总数据结构 二.访问标志 三.字段名称 四.字段描述符 五.属性项目数 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 三 ( 访问和修饰标志 | ...

最新文章

  1. C#中提供的精准测试程序运行时间的类Stopwatch
  2. Could not initialize class sun.awt.X11GraphicsEnvironment
  3. poj 2349 求MST中第S大的权值
  4. su user oracle does not exist,Linux下oracle用户无法su切换的异常【终极解决方案_生产环境亲测有效】...
  5. LSTM 与 Bilstm介绍(包含代码实现、Python)
  6. mysql8.0root连接失败_mac 下jdbc连接mysql 8.0 失败
  7. arduino的矩阵示例程序_用树莓派 Arduino 制造 LED 矩阵彩灯
  8. 电商平台系统架构设计案例分析
  9. 用数据分析看共享单车
  10. 在日软件工程师的远虑近忧
  11. SafeArea的使用
  12. 怎么关闭vivo系统自检_MIUI11系统已发布,你怎么能不会这个一键关闭广告的功能呢!...
  13. Microsoft Excel 最大行数各版本介绍
  14. VTK-Wight 之vtkIncrementalOctreePointLocator简介
  15. QCOM 8976 porting SPI device
  16. 视频监控摄像头直播主要应用领域分析
  17. 音视频编解码流程与如何使用 FFMPEG 命令进行音视频处理
  18. 为什么苹果要出7寸的iPad mini?
  19. 【深度学习】VGG16--slim
  20. JAVA 删除json中反斜杠_如何去除spring的ModelMap的返回类型的JSON序列化字符串中带有反斜杠符号...

热门文章

  1. Linux下使用g++编译C++程序——Compiling Cpp
  2. 主成分分析具体解释_主成分分析-现在用您自己的术语解释
  3. 明明知道银行存款会贬值,为什么还有那么多人把钱放在银行?
  4. 动态时间规整-DTW算法
  5. 创建 vxlan 并部署 instance - 每天5分钟玩转 OpenStack(147)
  6. Fedora telnet配置
  7. java实现三个数字的随机组合_JAVA编程实现随机生成指定长度的密码功能【大小写和数字组合】...
  8. 将excel转为python的字典_python读取excel表并把数据转存为字典
  9. java 什么时候依赖注入_玩框架java依赖注入 – 何时使用单例
  10. cad线性标注命令_CAD线性标注如何使用的