参考:Java 8 Stream Tutorial

介绍:什么是流、管道/Pipelinin、惰性/ laziness、部分地构造、流的一次性

1.什么是流

流是支持对数据序列进行Map/Reduce操作的数据序列的提供者和通道

  • 流 Stream<T>是元素集合的描述,而非列举。因而流的元素可以无限。
  • 流 Stream<T>是惰性计算的链表,(高阶函数)filter, map, reduce, find, match, sort等操作,如果不考虑开销,也可以用在Collections上——换言之,这些函数不是什么了不起的东西,而是 Stream<T>的惰性计算,才是要点。
  • 流对象只能够被终结/terminal函数消费一次

正则表达式(Regular Expression)是一种生成字符串的字符串,例如

String regEx="ab*";

"ab*"——表示了a、ab、abb、abbb……。可以说,正则表达式"ab*"表示了一个无限的集合。

流 Stream<T>表示类型为T的元素的序列,支持对元素的顺序或并发的聚合操作。Java的Collections类似{a、ab、abb、abbb},而流类似"ab*"。

3.5.1  Streams Are Delayed Lists

为Java带来了函数式编程风格的,并非lambda表达式,而是Stream API 。正如SICP所展示的,(高阶函数)filter, map, reduce, find, match, sort等操作,可以建立在list之上(但是开销太大),只有拥有 delayed evaluation /延迟计算/惰性计算的Stream ,才能够发挥上面的高阶函数的威力。

Stream<T>的操作,通常形容为管道/Pipelining,其实,也可以把String的操作称为管道。

        List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1", "c0");myList.stream().filter(s -> s.startsWith("c")).map(String::toUpperCase).sorted().forEach(System.out::println);boolean b = "a1".toUpperCase().endsWith("1");//比较 流的管道概念。

也可以看一看管道的慢动作:

        Stream<String> stream = myList.stream();Stream<String> filter = stream.filter(s -> s.startsWith("c"));Stream<String> map = filter.map(String::toUpperCase);Stream<String> sorted = map.sorted();sorted.forEach(System.out::println);

上面代码说明了,流的操作分为两种
Intermediate /中间的函数:程序中可以多次地、串接地调用流的 intermediate 操作,因为中间操作如映射map、过滤filter函数返回一个流对象。这类操作具有惰性/ laziness。虽然代码写出了对它们 的调用,但是并没有真正执行。

  • Stream<T> filter(Predicate<? super T> predicate);
  • <R> Stream<R> map(Function<? super T, ? extends R> mapper);
  • Stream<T> distinct();
  • Stream<T> sorted();
  • Stream<T> peek(Consumer<? super T> action);
  • Stream<T> limit(long maxSize);
  • Stream<T> skip(long n);
  • 一些创建Stream<T>的方法,Stream.iterate和generate,作为第一个调用。

Terminal /结尾的函数:对于流的串接调用,只能在最后加一个 terminal函数,正如调用String的 .endsWith("1")。结尾函数返回一个值或void函数产生一个副作用/side effect。 terminal函数的一个作用,在于唤醒前面的懒虫。

  • void forEach(Consumer<? super T> action);
  • Object[] toArray(); //重载
  • Optional<T> reduce(BinaryOperator<T> accumulator);  //重载
  • <R, A> R collect(Collector<? super T, A, R> collector);
  • long count();
  • boolean anyMatch(Predicate<? super T> predicate);//相关
  • Optional<T> findFirst();//Optional<T> findAny();

延迟计算+管道

流的一个重要特点:流总是部分地构造,并将该部分交给后面的函数处理;如果函数需要更多的数据,流会自动地构造新的数据。换言之,流像挤牙膏一样,一个个元素交给处理函数,给程序员的印象则是整个流的元素都存在。

为了观察流的处理管道,下面在处理函数中添加输出语句。

1.懒虫

    public static void m2() {Stream<String> of = Stream.of("c2", "a1",  "b0");Stream<String> map = of.map(s -> {System.out.println("map: " + s);//副作用return s.toUpperCase();});      }
执行m2()的代码,没有如何输出。执行完m2()后流对象of成为垃圾,of.map()并没有真正执行,因为没有尾函数唤醒中间函数map。
2.尾函数
    public static void m2() {Stream<String> of //同上System.out.println(map);System.out.println(map.count());}

添加尾函数count(),则输出:

java.util.stream.ReferencePipeline$3@31befd9f 
map: c2
map: a1
map: b0
3

3.部分地构造

如果添加的结尾函数为 forEach,注意:流对象of有3个元素,但是流对象map在懒惰中,开始的时候没有元素。

map.forEach()说:"map给我数据";

流对象map从流对象of中提取一个数据"c2"执行of.map();这时流对象map有了一个元素"C2";

forEach处理"C2";但是,map.forEach()要处理流对象map的所有元素,什么是所有?按照约定,of.map()将of对象的所有元素转换/map()后,形成流对象map的元素,因此,map的所有元素,个数即of对象的所有元素的个数。

    public static void m2() {Stream<String> of = Stream.of("c2", "a1", "b0");Stream<String> map = of.map(s -> {System.out.println("map: " + s);//副作用return s.toUpperCase();});//System.out.println(map);//System.out.println(map.count());map.forEach(s -> System.out.println("forEach: " + s)); }

于是,输出为:

map: c2
forEach: C2
map: a1
forEach: A1
map: b0

forEach: B0

练习:请解释下面代码的输出:(结尾函数找流对象filter,而流对象filter找流对象map 要数据)

    public static void m2() {Stream<String> of = Stream.of("c2", "a1","a2", "b0");Stream<String> map = of.map(s -> {System.out.println("map: " + s);//副作用return s.toUpperCase();});Stream<String> filter = map.filter(s -> {System.out.println("filter: " + s);return s.startsWith("A");});filter.forEach(s -> System.out.println("forEach: " + s));}

输出:

map: c2
filter: C2  //流对象filter没有"C2"这个元素
map: a1
filter: A1
forEach: A1
map: a2
filter: A2
forEach: A2
map: b0
filter: B0

ok,如果我们交换上面代码中map()和filter()函数的调用顺序,先filter()再map()(代码见下面),可以减少函数执行的次数

流的一次性

每一个流对象只能够被终结操作消费一次,如果在一个流对象上调用多次终结操作,将抛出java.lang.IllegalStateException。假设我们在打印流对象map的元素的同时,还要统计其个数,m3()代码抛出异常。java.lang.IllegalStateException: stream has already been operated upon or closed

    public static void m3() {Stream<String> map = Stream.of("c2", "a1", "a2", "b0");map.forEach(s -> System.out.println("forEach: " + s));        long count = map.count();//运行时异常}

如何复制一个流对象,完成多次终结操作呢?//Stream<String> map2 = map; //想都不用想

解决方案为:

  1. 使用stream supplier创建多个流;
  2. 还可以用String[]等保存辛辛苦苦计算的结果(collect 或toArray),再多次创建流对象。
import java.util.function.Supplier;
import java.util.stream.Stream;
// Util类public static final Supplier<Stream<String>> SS =  () -> Stream.of("a3","c2", "a1", "a2", "b0","c1", "a4").filter(s -> s.startsWith("a")).map(s -> s.toUpperCase()); //streamSupplier      public static final String[] arr =SS.get().toArray(String[]:: new) ;public static void m3() {       Stream.of(Util.arr).forEach(s -> System.out.println("forEach: " + s));pln("流中元素个数:"+Util.SS.get().count());          }

Java8:Stream概念相关推荐

  1. java8 Stream的实现原理 (从零开始实现一个stream流)

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  2. java双层list扁平化,浅谈java8 stream flatMap流的扁平化操作

    概念: Steam 是Java8 提出的一个新概念,不是输入输出的 Stream 流,而是一种用函数式编程方式在集合类上进行复杂操作的工具.简而言之,是以内部迭代的方式处理集合数据的操作,内部迭代可以 ...

  3. Java8——Stream流

    Java8--Stream流 Stream是数据渠道,用于操作集合.数组等生成的元素序列. Stream操作的三个步骤: 创建Stream 中间操作 终止操作 一.获取stream的四种方式 1.通过 ...

  4. java8 stream 教程_java8 Stream 流快速入门

    Stream 流 简介 Java 8 中,得益于 lambda 带来的函数式编程,引入了一个全新的 Stream流 概念,用于解决集合已有的弊端. 好处 我们先来看一个例子: 筛选出 names 中以 ...

  5. Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

    Java8 Stream 1 Stream概述 2 Stream的创建 3 Stream的使用 案例使用的员工类 3.1 遍历/匹配(foreach/find/match) 3.2 筛选(filter ...

  6. 20个实例玩转Java8 Stream

    20个实例玩转Java8 Stream 20个实例玩转Java8 Stream Stream概述 Stream的创建 stream和parallelStream的简单区分 stream的使用 遍历/匹 ...

  7. java8 stream原理

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  8. Java8 Stream 流的创建、筛选、映射、排序、归约、分组、聚合、提取与组合、收集、接合、foreach遍历

    目录 一  了解Stream 1 Stream概述 那么什么是Stream? Stream可以由数组或集合创建 Stream有几个特性: Stream流的起始操作 2 Stream的创建----Str ...

  9. Java8 Stream用法总结

    Java8 Stream Stream 总览 什么是流 流的构成 流的构造与转换 构造流的几种常见方法 流转换为其它数据结构 中间操作符 map mapToInt mapToLong.mapToDou ...

  10. JAVA8 Stream方法使用详解reduce、IntStream(二)

    文章目录 一 归约 1.元素求和 2.最大值和最小值 二.数值流 1.映射数值流 2.转换对象流 3.数值范围 三.构建流 1.由值创建流 2.由数组创建流 3.由文件生成流 4.由函数生成流 此章节 ...

最新文章

  1. AngularJS开发人员最常犯的10个错误
  2. 视觉slam学习--坐标系变换 | 欧式变换+仿射变换+射影变换
  3. 【机器学习】 - 关于Keras的深入理解
  4. 开源开放 | 开源网络通信行业知识图谱(新华三)
  5. 【ElasticSearch】Es 源码之 IndicesClusterStateService 源码解读
  6. eclipse工具的使用心得
  7. 零基础怎么开启编程之路 -(第1期)
  8. 医院耗材管理系统开发_15
  9. 推荐系统中传统模型——LightGBM + LR融合
  10. 2022-2028年全球与中国粮食种植行业市场深度调研及投资预测分析
  11. Python实战:将头像变成动漫风
  12. pythontrun什么意思_python 新手笔记一
  13. Excel基本操作 上篇
  14. 实现调用本地office打开在线文档功能
  15. Manjaro蓝牙连接问题
  16. 体脂秤方案开发脂肪秤方案设计
  17. 积分分离PID控制算法
  18. 小火狐进化_《乐贝星空》宠物大全 解析小火狐三阶进化
  19. 双剑合璧保障数据库安全
  20. Fiddler抓包工具是最强大最好用的 Web 调试工具之一

热门文章

  1. 特性二、Objective-C的Attributed属性
  2. java 使用POI简单excel表格导出,通过浏览器直接下载
  3. POI导出表格到浏览器工具类,poi工具类
  4. Python实现SLR(1)语法分析器,编译原理yyds!
  5. 网易云(网易蜂巢)对象存储--mp4播放
  6. 星宿UI文档手册(基于wordpress开源博客架构)
  7. c oracle fillschema,Oracle 19c create Sample Schemas
  8. 重型载货汽车驱动桥设计(UG三维图+CAD图纸+论文)
  9. AndroidX和Support库使用记录
  10. 列举独立开发者适用的数种游戏引擎