技术精讲:Java 8 Stream API

点击上方蓝色字体,选择“置顶公众号”

优质文章,第一时间送达

640?wx_fmt=jpeg

作者 |  谭朝红
链接 | ramostear.c om

Stream(流)是在Java 8中新增的新特性,首先需要为Java 8 Stream正名:Java 8中的Stream跟Java I/O Stream(例如:InputStream,OutputStream等)没有任何的关系。Stream是Java中数据源的包装器,通过Stream我们可以快速的对数据源进行操作(例如:过滤,排序、求和等等),且Stream不对任何数据进行存储,所以Stream也不是数据结构。

在Java 8中,Stream增强了Array,List等对象操作数据的能力,Stream提供了一些列的方法使的这些原有的集合类可以轻松创建Stream对象实例。通过Stream API提供的静态方法,可以快速的生成有限/无限的数据流。特别指出,Stream只是增强了原有集合类的能力,并未对底层的源码做任何的修改。

1.Stream的工作流程

接下来,将介绍使用Java Stream的基本步骤。在Java 8中,Stream的生命周期一共有三个阶段:

1.获取数据源并创建Stream实例。 数据源可以是数组、列表、对象或者I/O流
2.执行中间操作(Intermediate Operations)。 中间操作可以是过滤、排序、类型转换等操作
3.执行终端操作(Terminal Operation)。

终端操作主要是对最终结果进行计数、求和、创建新集合等操作

图1-1展示了Java 8 Stream的工作工作流程。

640

图 1-1 Stream工作流程

2.Stream 操作管道是什么?

所谓的Stream操作管道是指“中间操作”和“终端操作”的组合。在Java 8 Stream API中,许多的操作方法都将当前的Stream作为最终结果返回,这就允许开发人员以连式编程的方式,组合出更大的Stream操作管道。Java 8 Stream API位于java.util.stream包下,其组织结构如图2-1所示:

640

图2-1 java.util.stream包

接下来,将详细介绍“中间操作”和“终端操作”各自的方法和特性。

Stream的中间操作将返回一个Stream,它允许开发者以查询的方式调用多个其他的操作。特别注意的是,在“终端操作”方法未被调用之前,“中间操作”的方法不会被执行。Stream的“中间操作”一共包括七个:Stream.filter,Stream.map,Stream.flatmap,Stream.peek,Stream.sorted和Stream.limit。

3.1.Stream.filter

Stream.filter将返回一个包含与之谓词相匹配的元素的新的Stream。下面将通过一个示例,演示Stream.filter的使用方法。

640

执行结果:

640

此示例使用“中间操作”Stream.filter过滤以“j”开头的字符串,并使用“终端操作”Stream.count统计以“j”开头的字符串数量。

3.2.Stream.map

Stream.map将使用java.util.function.Function提供的方法转换Stream中的元素。Stream.map操作通常用于转换集合对象。下面是使用Stream.map的示例代码:

640

执行结果:

640

3.3.Stream.flatmap

Stream.flatmap通常将Stream中的每一个元素转换成0个或多个元素。下面将通过flatmap来统计一个文本中单词的出现次数(不重复)。示例代码如下:

640

执行结果:

640

source.txt内容:

 
 

3.4.Stream.peek

Stream.peek在调试期间非常有用,它允许您在操作Stream之前查看Stream内部的数据。下面是示例代码:

640

执行结果:

Java	
C#	
C++	
GO	
result size = 4

3.5.Stream.distinct

Stream.distinct是根据其内部的equals方法在Stream中对元素进行去重操作。下面是使用示例:

640

执行结果:

6	
1	
8	
11

3.6.Stream.sorted

Stream.sorted方法用于将Stream中的数据元素进行排序。下面是使用示例:

640

执行结果:

1	
6	
8	
11

3.7.Stream.limit

Stream.limit方法用于限定Stream中元素的个数。下面是使用示例:

640

执行结果:

0	
1	
2	
3	
4	
5	
11	
618

4.Stream 终端操作(Terminal  Operations)是什么?

终端操作用于生产最终的数据结果,如对象,数组或者列表。在终端操作方法被执行前,中间操作方法将不会被执行。终端操作方法一共有十二个,它们是:forEach,toArray,reduce,collect,min,max,count,anymatch,allMatch,noneMatch,findFirst和findAny。表4-1列举了这十二个终端操作的使用方法

640

图 4-1 Stream终端操作方法

5.如何创建Stream实例?

Stream支持多种数据源创建Stream实例,如Array,List,Object和I/O流。接下来,通过简单的示例来演示如何创建Stream。

5.1 使用数组创建Stream

首先,我们定义一个静态的数组users,并初始几条用户数据,代码如下:

private static User[] users = {new User("user1"),new User("user2"),new User("user3")};

接下来,将利用users来创建一个Stream对象实例,示例代码如下:

public static void fromArray() {	
    Stream<User> uStream = Stream.of(users);	
    System.out.println("create stream from array.");	
    uStream.forEach(u->System.out.println(u.getUsername()));	
}

5.2 使用对象创建Stream

沿用上面定义的users数组,通过数组下标获得用户对象,并利用这些对象创建一个Stream实例,代码如下:

public static void fromObjects() {	

	
    Stream<User> uStream = Stream.of(users[0],users[1],users[2]);	
    System.out.println("create stream from objects.");	
    uStream.forEach(u->System.out.println(u.getUsername()));	

	
}

5.3 使用List创建Stream

接下来,我们将users数组转换成List,并使用此List来创建Stream实例。代码如下:

public static void fromList() {	

	
    List<User> list = Arrays.asList(users);	
    Stream<User> uStream = list.stream();	
    System.out.println("create stream from list.");	
    uStream.forEach(u->System.out.println(u.getUsername()));	

	
}

5.4 使用builder方法创建Stream

最后,我们将演示使用Stream内置的builder()静态方法创建Stream实例。代码如下:

public static void byBuilder() {	
    Stream.Builder<User> userStream = Stream.builder();	
    userStream.accept(users[0]);	
    userStream.accept(users[1]);	
    userStream.accept(users[2]);	
    System.out.println("create stream by builder.");	
    userStream.build().forEach(u->System.out.println(u.getUsername()));	
}

以上就是通过数据源创建Stream实例的几种不同方式。

6.为什么要使用Stream?

在命令式编程中,我们必须逐行编写代码,才能完成相关的计算。例如,计算1~10000的数的总和,我们需要使用for(int i=1;i<=10000;i++){….}循环语句来迭代求值。在这个语句中,我们需要花费额外的精力来维护loop变量的值。而在声明式编程中,我们只需要关注想要实现的目标,而不是其内部重复的循环逻辑。

Stream API通过使用内部迭代器和lambda表达式,帮助我们完成了那些重复性的逻辑。Stream API的作用不仅仅用于数据迭代,它还能通过中间操作和终端操作实现数据的排序、类型转换、求和、计数、求最大值、最小值以及模式匹配等操作。

接下来,表6-1展示了命令式编程和声明式编程的优劣:

640

总结

在本文中,介绍了Java Stream的基本工作流程,并详细列举了中间操作和终端操作的细节。通过使用Stream,我们可以将传统的命令式编程代码进行简化,以提高编程效率。

如果喜欢本篇文章,欢迎转发、点赞。关注订阅号「Web项目聚集地」,回复「全栈」即可获取 2019 年最新 Java、Python、前端学习视频资源。


推荐阅读

1. 

2. 

3. 

4. 

5. 

6. 

7. 

8. 

640?wx_fmt=jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值