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 分钟前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
android·java·kotlin
Java技术小馆10 分钟前
如何设计一个本地缓存
java·面试·架构
XuanXu1 小时前
Java AQS原理以及应用
java
风象南4 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio13 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室18 小时前
java日常开发笔记和开发问题记录
java
咖啡教室18 小时前
java练习项目记录笔记
java
鱼樱前端18 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea19 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq