Java8实战-总结17

Java8实战-总结17

引入流

流操作

java.util.stream.Stream中的Stream接口定义了许多操作。它们可以分为两大类。再来看一下前面的例子:

java 复制代码
List<String> names = menu.stream() //从菜单获得流 
				.filter(d -> d.getcalories()> 300) //中间操作
				.map(Dish::getName)//中间操作
				.limit(3)//中间操作
				.collect(toList());//将Stream转换为List

可以看到两类操作:

  • filter、maplimit可以连成一条流水线;
  • collect触发流水线执行并关闭它。

可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作。

中间操作

诸如filtersorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。**重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。**这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。

为了搞清楚流水线中到底发生了什么,把代码改一改,让每个Lambda都打印出当前处理的菜肴(就像很多演示和调试技巧一样,这种编程风格要是搁在生产代码里那就吓死人了,但是学习的时候却可以直接看清楚求值的顺序):

java 复制代码
List<String> names = menu.stream()
						.filter(d -> {
							System.out.println("filtering" + d.getName()); //打印当前筛选的菜肴
							return d.getcalories() > 300;
						})
						.map(d -> {
							System.out.println("mapping" + d.getName());//提取菜名时打印出来
							return d.getName();
						})
						.limit(3)
						.collect(toList());
System.out.println(names);

此代码执行时将打印:

java 复制代码
filtering pork
mapping pork
filtering beef
mapping beef
filtering chicken
mapping chicken
[pork, beef, chicken]

有好几种优化利用了流的延迟性质。第一,尽管很多菜的热量都高于300卡路里,但只选出了前三个。这是因为limit操作和一种称为短路的技巧,第二,尽管filtermap是两个独立的操作,但它们合并到同一次遍历中了。

终端操作

终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如ListInteger,甚至void。例如,在下面的流水线中,forEach是一个返回void的终端操作,它会对源中的每道菜应用一个Lambda。把System.out.println传递给forEach,并要求它打印出由menu生成的流中的每一个Dish:

java 复制代码
menu.stream().forEach(System.out::println);
测验:中间操作与终端操作
在下列流水线中,找出中间操作和终端操作

long count = menu.stream()
		.filter(d -> d.getCalories()> 300)
		.distinct()
		.limit(3)
		.count();
		
答案:流水线中最后一个操作count返回一个long,这是一个非Stream的值。因此它是一个终端操作。所有前面的操作,filter、distinct、limit,都是连接起来的,并返回一个Stream,因此它们是中间操作。

使用流

总而言之,流的使用一般包括三件事:

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

流的流水线背后的理念类似于构建器模式。在构建器模式中有一个调用链用来设置一套配置(对流来说这就是一个中间操作链),接着是调用built方法(对流来说就是终端操作)。

下表总结了前面在代码例子中看到的中间流操作和终端流操作。请注意这并不能涵盖Stream API提供的操作。

小结

以下是一些关键概念:

  • 流是"从支持数据处理操作的源生成的一系列元素"。
  • 流利用内部迭代:迭代通过filtermapsorted等操作被抽象掉了。
  • 流操作有两类:中间操作和终端操作。
  • filtermap等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流水线,但并不会生成任何结果。
  • forEachcount等终端操作会返回一个非流的值,并处理流水线以返回结果。
  • 流中的元素是按需计算的。
相关推荐
疯一样的码农5 分钟前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
代码之光_19806 分钟前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi12 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
&岁月不待人&28 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove31 分钟前
G1垃圾回收器日志详解
java·开发语言
对许35 分钟前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j
无尽的大道39 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒42 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
小鑫记得努力1 小时前
Java类和对象(下篇)
java
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git