一、引入流(Stream)

1、什么是流

流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了!

假设有这么一个需求,要从菜单中选出热量小于400的并排序,输出菜名,下面介绍两种做法,方法一是传统方式,方法二是使用java8方式

/*** 菜单类*/
@Data
@AllArgsConstructor
public class Dish {private String name;private boolean vegetarian;private int calories;private Type type;public enum Type {MEAT, FISH, OTHER}
}
public class DishService {public static void main(String[] args) {test1();test2();}/*** 传统方式*/private static void test1() {//返回低热量的菜肴名称的,并按照卡路里排序List<Dish> menu = init();List<Dish> lowCaloricDishes = new ArrayList<>();//筛选出低于400的for (Dish dish : menu) {if (dish.getCalories() < 400) {lowCaloricDishes.add(dish);}}//排序Collections.sort(lowCaloricDishes, new Comparator<Dish>() {@Overridepublic int compare(Dish o1, Dish o2) {return Integer.compare(o1.getCalories(),o2.getCalories());}});//获取菜名List<String> lowCaloricDishesName = new ArrayList<>();for (Dish lowCaloricDish : lowCaloricDishes) {lowCaloricDishesName.add(lowCaloricDish.getName());}System.out.println(lowCaloricDishesName);}/*** java8方式*/private static void test2() {//返回低热量的菜肴名称的,并按照卡路里排序List<Dish> menu = init();List<String> collect = menu.stream().filter(m -> m.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories)).map(Dish::getName).collect(Collectors.toList());System.out.println(collect);}private static List<Dish> init() {List<Dish> menu = Arrays.asList(new Dish("猪肉", false, 800, Dish.Type.MEAT),new Dish("牛肉", false, 700, Dish.Type.MEAT),new Dish("鸡肉", false, 400, Dish.Type.MEAT),new Dish("白菜", true, 530, Dish.Type.OTHER),new Dish("菠菜", true, 350, Dish.Type.OTHER),new Dish("时令水果", true, 120, Dish.Type.OTHER),new Dish("黄瓜", true, 550, Dish.Type.OTHER),new Dish("对虾", false, 300, Dish.Type.FISH),new Dish("三文鱼", false, 450, Dish.Type.FISH));return menu;}
}
从上面代码中有几个显而易见的好处:
1、代码是以声明性方式来写的:说明想要完成什么而不是说明如何实现一个操作(利用循环和if条件等控制语句)。
2、可以把几个基础操作链接起来来表达复杂的数据处理流水线(在filter后面接上 sorted、map和collect操作)同时保持代码清晰可读。filter的结果被传给了sorted方法,再传给map方法,后传给collect方法。

filter、sorted、map和collect等操作是与具体线程模型无关的高层次构件,所以它们的内部实现可以是单线程的,也可能透明地充分利用你的多核架构!在实践中,这意味着你用不着为了让某些数据处理任务并行而去操心线程和锁了,Stream API都替你做好了!
Java 8中的Stream API好处:

2、流简介

1、简短的定义:从支持数据处理操作生成的元素序列
元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。但流的目的在于表达计算,比如前面见到的 filter、sorted和map。集合讲的是数据,流讲的是计算。
源:流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。
2、流操作的两个重要特点
流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。流水线的操作可以看作对数据源进行数据库式查询。
内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。
private static void test2() {List<Dish> menu = init();List<String> collect = menu.stream()//从menu获得流.filter(m -> m.getCalories() > 300)//建立操作流水线.map(Dish::getName).limit(3).collect(Collectors.toList());//将结果保存到另一个List中System.out.println(collect);
}
在上面代码中,先是对menu调用stream方法,由菜单得到一个流。数据源是菜肴列表(菜 单),它给流提供一个元素序列。接下来,对流应用一系列数据处理操作:filter、map、limit 和collect。除了collect之外,所有这些操作都会返回另一个流,这样它们就可以接成一条流 水线,于是就可以看作对源的一个查询。最后,collect操作开始处理流水线,并返回结果(它和别的操作不一样,因为它返回的不是流,在这里是一个List)。在调用collect之前,没有任何结果产生,实际上根本就没有从menu里选择元素。可以这么理解:链中的方法调用都在排队等待,直到调用collect。

filter:接受一个Lambda,从流中排除某些元素。
map:接受一个Lambda,将元素转换成其他形式或提取信息。
limit:截断流,使其元素不超过给定数量。
collect:将流转换为其他形式。可以把collect看作能够接受各种方案作为参数,并将流中的元素累积成为一个汇总结果的操作。

3、流与集合

1、集合是一个内存中的数据结构,包含数据结构中目前所有的值,也就是说集合中的值都要先计算好才能够放入集合中,但是流则不同,流是概念上固定的数据结构其元素是按需计算的,不能添加或者删除元素,只有在需要的时候才将需要的流计算出来。集合需要提前将值全部准备好而流则是将值准备一部分。
2、和迭代器类似,流只能遍历一次。遍历完之后,这个流已经被消费掉了。 你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样。遍历多次会抛出stream has already been operated upon or closed异常。
3、集合和流的一个区别则是遍历数据的方式,使用Collection接口需要用户去进行迭代,也就是在集合的外部这称为外部迭代。而Streams则使用内部迭代,它会代替使用者在流的内部进行迭代,还会把流值存在某个地方,只需要通过函数进行操控就可以了,Streams的内部迭代还可以自动选择一种适合计算机硬件的数据表示和并行实现。同时流只能够遍历一次。
public class Test04 {public static void main(String[] args) {List<String> title = Arrays.asList("Java8", "In", "Action");Stream<String> s = title.stream();s.forEach(System.out::println);s.forEach(System.out::println);}
}
抛出异常:stream has already been operated upon or closed
总结:

二、流操作

1、分类

java.util.stream.Stream中的Stream接口定义了许多操作。它们可以分为两大类:中间操作终端操作

2、中间操作与终端操作

1、中间操作:可以连接起来的流操作称为中间操作,如:filter、map和limit可以连成一条流水线。中间操作会返回另一个流,这让多个操作可以连接起来形成一个查询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理(它们很懒)。这是因为中间操作一般都是可以合并起来,在终端操作时一次性全部处理。
2、终端操作:关闭流的操作称为终端操作。 终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如List、Integer、甚至void。

3、使用流步骤

使用流三部曲:

(二)流(Stream)相关推荐

  1. 【JAVA SE】第十二章 流(Stream)、IO流和文件(File)

    第十二章 流(Stream).IO和文件(File) 文章目录 第十二章 流(Stream).IO和文件(File) 一.流(Stream) 1.什么是流 2.流的分类 二.IO流 1.字节输入流(I ...

  2. 【Java】流(Stream)快速入门

    本文是面向Java初学者的流(Stream)学习指导教程.文章内容偏向怎么用,而弱化其诞生背景.流的概念.内部原理等说明解释性的语段. 主要内容: Optional 创建流 操作流 收集流 目录 什么 ...

  3. Java8初体验(二)Stream语法详解(转)

    本文转自http://ifeve.com/stream/ Java8初体验(二)Stream语法详解 感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com 上篇文章Java8初体验( ...

  4. C语言ftell()函数(返回文件当前位置)(返回给定流 stream 的当前文件位置)

    C 标准库 - <stdio.h> 描述 C 库函数 long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置. 声明 下面是 ftell() 函 ...

  5. c语言fgetc()函数(从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动)

    C 标准库 - <stdio.h> 文章目录 描述 声明 参数 返回值 实例 描述 C 库函数 int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一 ...

  6. C语言fputs()函数(把字符串写入到指定的流 stream (文件指针)中)

    C 标准库 - <stdio.h> 注意:fgets和fputs是字符串读写,fread和fwrite是数据块读写 文章目录 描述 声明 参数 返回值 实例 描述 C 库函数 int fp ...

  7. C语言函数fscanf()(从流 stream 读取格式化输入)(如果成功,返回成功匹配和赋值的个数;否则返回EOF)(分隔符:space、Tab、Enter)

    C 标准库 - <stdio.h> 文章目录 描述 声明 参数 返回值 实例1 实例2 实例3 注意(分隔符:space.Tab.Enter) 换行格式化输入示例(暂时只能弄英文字符,中文 ...

  8. C语言rewind()函数(设置文件位置为给定流 stream 的文件的开头)(回到文件开头重读)

    C 标准库 - <stdio.h> 文章目录 描述 声明 参数 返回值 实例 描述 C 库函数 void rewind(FILE *stream) 设置文件位置为给定流 stream 的文 ...

  9. Java 方法、 流(Stream)、文件(File)和IO 总结

    这里只总结几个要点. 1. 方法的命名规则 1.方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符.例如:addPerson. 2.下划线可能出现在 JUnit 测 ...

  10. redis 流 stream的使用总结 - 消费者组

    本博客讲述如何使用redis中流stream的组 简言 1. 消费者组(consumer group)允许用户将一个流从逻辑上分成多个不同的流,并让消费者组组下的消费者去处理组中的消息 2. 多个消费 ...

最新文章

  1. poj 2337 Catenyms 【欧拉路径】
  2. 集成ShareSDK,分享成功后QQ和空间回调不执行的可能原因
  3. PowerDesigner的安装
  4. 从无到有算法养成篇-线性表实战篇
  5. java excel data 导入数据_java实现导入导出excel数据
  6. hive job oom
  7. java例程练习(一维数组)
  8. Oracle入门(五D)之如何设置show parameter显示隐含参数
  9. 苹果关掉200m限制_苹果下载大于200M限制
  10. 842. 将数组拆分成斐波那契序列
  11. linux\虚拟机\docker如何查看操作系统版本、内核、cup等信息
  12. 前端实现Word在线预览
  13. UGUI制作Tab标签页
  14. 一个检测PC机软硬件系统信息的工具——EVEREST
  15. TensorFlow Probability 联合分布变分推断工具,估计权重的贝叶斯可信区间更简单
  16. 三方平台管理公众号----创建第三方平台
  17. 给定桩号获取纵断面中的高程值(c# for civil3d)
  18. 图论在识别人脑网络连通性模式中的应用
  19. 【UML】-- 用例图练习题含答案(订餐系统、远程网络教学系统、交互式网络系统)
  20. IMD sweet Spot(一)

热门文章

  1. Codepen 每日精选(2018-3-26)
  2. FLINK提交任务的两种方式
  3. 计算机总是莫名其妙重启,电脑老是自动重启怎么办,电脑为什么经常自动重启_系统圣地...
  4. 计算机领域顶级会议列表
  5. 明翰英语教学系列之句法篇V0.6(持续更新)
  6. c语言中函数已有主体,error C2084 函数已有主体(Function already has a body)解决方案...
  7. 【NABCD需求分析】Time Shaft·时间轴
  8. RAW 图像格式转换工具 bayer2rgb
  9. 使用itextPdf合并PDF
  10. 2013武汉住房公积金新政详解