java stream 原理

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> { }

原文地址:https://www.cnblogs.com/dragonfei/p/8710240.html

时间: 2024-09-30 15:52:39

java stream 原理的相关文章

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

Java NIO原理 图文分析及代码实现 博客分类: java底层 java NIO原理阻塞I/O非阻塞I/O Java NIO原理图文分析及代码实现 前言:  最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.可以参考:http://baike.baidu.com/view/32726.htm )机制时,发现hadoop的RPC机制的实现主要用到了两个技术

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

转http://weixiaolu.iteye.com/blog/1479656 目录: 一.java NIO 和阻塞I/O的区别      1. 阻塞I/O通信模型      2. java NIO原理及通信模型 二.java NIO服务端和客户端代码实现 具体分析: 一.java NIO 和阻塞I/O的区别 1. 阻塞I/O通信模型 假如现在你对阻塞I/O已有了一定了解,我们知道阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超 时)才会返回:同

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

目录:一.java NIO 和阻塞I/O的区别     1. 阻塞I/O通信模型     2. java NIO原理及通信模型二.java NIO服务端和客户端代码实现 具体分析:  一.java NIO 和阻塞I/O的区别 1. 阻塞I/O通信模型 假如现在你对阻塞I/O已有了一定了解,我们知道阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回:同样,在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会

《Java虚拟机原理图解》1.4 class文件中的字段表集合--field字段在class文件中是怎样组织的

0.前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述,很难给人以形象化的认知,看完之后感觉还是稀里糊涂的. 感于以上的种种,我打算把我在学习JVM虚拟机的过程中学到的东西,结合自己的理解,总结成<Java虚拟机原理图解> 这个系列,以图解的形式,将抽象的JVM虚拟机的知识具体化,希望能够对想了解Java虚拟机原理的的Java程序员 提供点帮助. 读

Java Annotation原理分析(一)

转自:http://blog.csdn.net/blueheart20/article/details/18725801 小引: 在当下的Java语言层面上,Annotation已经被应用到了语言的各个方面,它已经在现在的ssh开发中,通过Annotation极大的提高了开发的效率,堪称开发神器.在这篇文章中,我们来了解一下的Annotation在Java中的前身今世吧. 1.   Java Annotation因何而来? 最初从印象中,是可以替代之前JDK1.4开发中,大量繁琐的配置项,Ann

《Java虚拟机原理图解》4.JVM机器指令集

0. 前言 Java虚拟机和真实的计算机一样,执行的都是二进制的机器码:而我们将.java 源码编译成.class 文件,class文件便是Java虚拟机可以认识的二进制机器码,Java可以识别class文件里的信息和机器指令,进而执行这些机器指令. 那么,Java虚拟机是怎样执行这些二进制的机器码的呢? 本文将通过一个很easy的样例,带你感受一下Java虚拟机执行机器码的过程和其工作的基本原理. 读完本文,你将会了解到: 1.Java虚拟机对执行时虚拟机栈(JVM Stack) 的组织 2.

Java Stream 使用详解

Stream是 Java 8新增加的类,用来补充集合类. Stream代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的. Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的数据的访问和有效管理(增删改),而Stream并没有提供访问和管理元素的方式,而是通过声明数据源的方式,利用可计算的操作在数据源上执行,当然BaseStream.iterator()和BaseStream.spliterator()操作提供了遍历元素的方法. Java Stream提供了提供了串行和

Java虚拟机原理图解-- 1.2.2、Class文件中的常量池详解(上)[转]

NO1.常量池在class文件的什么位置? 我的上一篇文章<Java虚拟机原理图解> 1.class文件基本组织结构中已经提到了class的文件结构,在class文件中的魔数.副版本号.主版本之后,紧接着就是常量池的数据区域了,如下图用红线包括的位置: 知道了常量池的位置后,然后让我们来揭秘常量池里究竟有什么东西吧- NO2.常量池的里面是怎么组织的? 常量池的组织很简单,前端的两个字节占有的位置叫做常量池计数器(constant_pool_count),它记录着常量池的组成元素  常量池项(

Java虚拟机原理图解-- 1.1、class文件基本组织结构 [转]

作为Java程序猿,我们知道,我们写好的.java 源代码,最后会被Java编译器编译成后缀为.class的文件,该类型的文件是由字节组成的文件,又叫字节码文件.那么,class字节码文件里面到底是有什么呢?它又是怎样组织的呢?让我们先来大概了解一下他的组成结构吧. NO1. 魔数(magic) 所有的由Java编译器编译而成的class文件的前8个字节都是“0xCAFEBABE”        它的作用在于:当JVM在尝试加载某个文件到内存中来的时候,会首先判断此class文件有没有JVM认为