1. 流简介

流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理。让我们来看一个实例返回低热量(<400)的菜肴名称:

Java7版本:
List<Dish> lowCaloricDishes = new ArrayList<>();
// 用累加器筛选元素
for(Dish d: menu){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
// 用匿名类对菜肴排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
// 处理排序后的菜名列表
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
Java8版本:
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName = menu.stream()
.filter(d -> d.getCalories() < 400) // 选出400卡路里以下的菜肴
.sorted(comparing(Dish::getCalories)) // 按照卡路里排序
.map(Dish::getName) // 提取菜肴名称
.collect(toList()); // 将所有的名称保存在List中
利用多核架构并行执行,只需要把stream()换成parallelStream()

Java 8中的Stream API特性:

  • 声明性——更简洁,更易读
  • 可复合——更灵活
  • 可并行——性能更好

流定义:

  • 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序 值。
  • 源——流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集 合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
  • 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。
  • 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。这让我们下一章中的一些优化成为可能,如延迟和短路。流水线的操作可以看作对数据源进行数据库式查询。
  • 内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

2. 流与集合

集合与流之间的差异就在于什么时候进行计算。集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。相比之下,流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。集合和流的另一个关键区别在于它们遍历数据的方式。

2.1 只能遍历一次

和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。以下代码会抛出一个异常,说流已被消费掉了:

List<String> title = Arrays.asList(“Java8”,”In”, “Action”);
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println); Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.lujiahao.learnjava8.chapter4.StreamAndCollection.main(StreamAndCollection.java:16)

2.2 外部迭代与内部迭代

使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。相反,Streams库使用内部迭代

集合:用for-each循环外部迭代
List<String> names = new ArrayList<>();
for(Dish d: menu){
names.add(d.getName());
} 集合:用背后的迭代器做外部迭代
List<String> names = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
while(iterator.hasNext()) {
Dish d = iterator.next();
names.add(d.getName());
} 流:内部迭代
List<String> names = menu.stream()
.map(Dish::getName)
.collect(toList());

3. 流操作

java.util.stream.Stream中的Stream接口定义了许多操作。它们可以分为两大类。可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作。

中间操作:除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。

终端操作:会从流的流水线生成结果。其结果是任何不是流的值。

流的使用一般包括三件事:

  • 一个数据源(如集合)来执行一个查询;
  • 一个中间操作链,形成一条流的流水线;
  • 一个终端操作,执行流水线,并能生成结果。

流的流水线背后的理念类似于构建器模式。

常见流操作:

4. 小结

以下是你应从本章中学到的一些关键概念。

  • 流是“从支持数据处理操作的源生成的一系列元素”。
  • 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象掉了。
  • 流操作有两类:中间操作和终端操作。
  • filter和map等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流水线,但并不会生成任何结果
  • forEach和count等终端操作会返回一个非流的值,并处理流水线以返回结果。
  • 流中的元素是按需计算的。

资源获取

  • 公众号回复 : Java8 即可获取《Java 8 in Action》中英文版!

Tips

  • 欢迎收藏和转发,感谢你的支持!(๑•̀ㅂ•́)و✧
  • 欢迎关注我的公众号:庄里程序猿,读书笔记教程资源第一时间获得!

《Java 8 in Action》Chapter 4:引入流的更多相关文章

  1. 《Java 8 in Action》Chapter 5:使用流

    流让你从外部迭代转向内部迭代,for循环显示迭代不用再写了,流内部管理对集合数据的迭代.这种处理数据的方式很有用,因为你让Stream API管理如何处理数据.这样Stream API就可以在背后进行 ...

  2. 《Java 8 in Action》Chapter 1:为什么要关心Java 8

    自1998年 JDK 1.0(Java 1.0) 发布以来,Java 已经受到了学生.项目经理和程序员等一大批活跃用户的欢迎.这一语言极富活力,不断被用在大大小小的项目里.从 Java 1.1(199 ...

  3. 《Java 8 in Action》Chapter 9:默认方法

    传统上,Java程序的接口是将相关方法按照约定组合到一起的方式.实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现. 但是,一旦类库的设计者需要更新接口,向其中加入新的方法, ...

  4. 《Java 8 in Action》Chapter 10:用Optional取代null

    1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法.ALGOL W是第一批在堆上分配记录的类型语言之一.Hoare选择null引用这种方式,& ...

  5. 《Java 8 in Action》Chapter 11:CompletableFuture:组合式异步编程

    某个网站的数据来自Facebook.Twitter和Google,这就需要网站与互联网上的多个Web服务通信.可是,你并不希望因为等待某些服务的响应,阻塞应用程序的运行,浪费数十亿宝贵的CPU时钟周期 ...

  6. 《Java 8 in Action》Chapter 2:通过行为参数化传递代码

    你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入 Lambda表达式的主要原因.行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式.一言以蔽之,它意味 着拿出一个代码 ...

  7. 《Java 8 in Action》Chapter 3:Lambda表达式

    1. Lambda简介 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名--我们说匿名,是因为 ...

  8. 《Java 8 in Action》Chapter 12:新的日期和时间API

    在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类.同时这个类还有两个很大的缺点:年份的起始选择是1900年,月份的起始从0开始. 在Java 1.1中,Date类中的很多 ...

  9. Struts2基础-1- 简单java类实现Action控制器

    Strut2中,Action可以不继承任何特殊的类或不实现任何特殊的接口,可以只编写一个普通的Java类作为Action类,只要该类含有一个返回字符串的无参的public方法即可!实际开发中,通常继承 ...

随机推荐

  1. 8086 IO读写操作

    如图所示,通过8086来读写io口,实现流水灯以及开关.本电路是基于8086最小模式下的三总线结构添加的,三总线结构原理较为复杂本篇就不对其原理进行介绍了,大家可以自行查阅相关引脚的功能从而实现. 本 ...

  2. Hive的查询

    基本查询 全表和特定列查询 1.全表查询 select * from emp; 2.选择特定列查询 select empno,ename from emp; 注意: 1.SQL语言大小写不敏感 2.S ...

  3. vscode如何将less编译到指定css目录中

    首先使用vscode的搬砖猿,想要在vscode编辑器里面很方便的编译less文件,需要在扩展商店里面下载一款名叫Esay LESS的超好用扩展(我自己都不信),哈哈,其实还好. 安装完扩展之后,最好 ...

  4. python递归-三元表达式-列表生成式-字典生成式-匿名函数-部分内置函数-04

    递归 递归: # 函数在调用阶段直接或间接地又调用了自身 应用场景: # 将列表中的数字依次打印出来(循环的层数是你必须要考虑的点)   -->  l = [1, [2, [3, [4, [5, ...

  5. element 上传组件 el-upload 的经验总结

    前言 最近在做后台管理项目,采用的 vue-element-admin ,上传图片是一个很常用的功能,也遇到了很多问题,刚好趁此机会做一些总结. 初步总结下会提到的问题,目录如下: el-upload ...

  6. LinkedList实现类

    List还有一个LinkedList的实现,它是一个基于链表实现的List类,对于顺序访问集合中的元素进行了优化,特别是当插入.删除元素时速度非常快.因为LinkedList即实现了List接口,也实 ...

  7. 努力做一个优秀的programmer [ C# 影院售票系统]

    Cinema.cs类 [Serializable] // 电影院类 public class Cinema { public Cinema() { //二进制 SoldTickets = new Li ...

  8. 100天搞定机器学习|Day9-12 支持向量机

    机器学习100天|Day1数据预处理 100天搞定机器学习|Day2简单线性回归分析 100天搞定机器学习|Day3多元线性回归 100天搞定机器学习|Day4-6 逻辑回归 100天搞定机器学习|D ...

  9. 一份关于.NET Core云原生采用情况调查

    调查背景 Kubernetes 越来越多地在生产环境中使用,围绕 Kubernetes 的整个生态系统在不断演进,新的工具和解决方案也在持续发布.云原生计算的发展驱动着各个企业转向遵循云原生原则(启动 ...

  10. 洛谷 P5150 题解

    题面 因为 n=lcm(a,b)n = lcm(a, b)n=lcm(a,b) ,可以得出: a  和 b  的质因数都是 n 的质因数 对于 n  的每个质因数 x ,在 n 中的次数为 y ,那么 ...