Java8新特性之三:Stream API

简介: Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA中,也已经加入了Stream。

Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA中,也已经加入了Stream。如:

复制代码
1 @Query("select u from User u")
2 Stream findAllByCustomQueryAndStream();
3
4 Stream readAllByFirstnameNotNull();
5
6 @Query("select u from User u")
7 Stream streamAllPaged(Pageable pageable);
复制代码
Stream API给我们操作集合带来了强大的功用,同时Stream API操作简单,容易上手。

1、Stream的操作步骤
Stream有如下三个操作步骤:

一、创建Stream

从一个数据源,如集合、数组中获取流。

二、中间操作

一个操作的中间链,对数据源的数据进行操作。

三、终止操作

一个终止操作,执行中间操作链,并产生结果。

要注意的是,对流的操作完成后需要进行关闭操作(或者用JAVA7的try-with-resources)。

举个简单的例子:

假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。

复制代码
1 @Data
2 class Person {
3 private String name;
4 private Integer age;
5 private String country;
6 private char sex;
7
8 public Person(String name, Integer age, String country, char sex) {
9 this.name = name;
10 this.age = age;
11 this.country = country;
12 this.sex = sex;
13 }
14 }
复制代码
复制代码
1 List personList = new ArrayList<>();
2 personList.add(new Person("欧阳雪",18,"中国",'F'));
3 personList.add(new Person("Tom",24,"美国",'M'));
4 personList.add(new Person("Harley",22,"英国",'F'));
5 personList.add(new Person("向天笑",20,"中国",'M'));
6 personList.add(new Person("李康",22,"中国",'M'));
7 personList.add(new Person("小梅",20,"中国",'F'));
8 personList.add(new Person("何雪",21,"中国",'F'));
9 personList.add(new Person("李康",22,"中国",'M'));
复制代码
在JDK8以前,我们可以通过遍历列表来完成。但是在有了Stream API后,可以这样来实现:

复制代码
1 public static void main(String[] args) {
2
3 // 1)找到年龄大于18岁的人并输出;
4 personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);
5
6 System.out.println("-------------------------------------------");
7
8 // 2)找出所有中国人的数量
9 long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
10 System.out.println("中国人有:" + chinaPersonNum + "个");
11 }
复制代码
输出结果:

复制代码
Person(name=Tom, age=24, country=美国, sex=M)
Person(name=Harley, age=22, country=英国, sex=F)
Person(name=向天笑, age=20, country=中国, sex=M)
Person(name=李康, age=22, country=中国, sex=M)
Person(name=小梅, age=20, country=中国, sex=F)
Person(name=何雪, age=21, country=中国, sex=F)

Person(name=李康, age=22, country=中国, sex=M)

中国人有:6

复制代码
在这个例子中,personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。

2、Stream中间操作--筛选与切片
filter:接收Lambda,从流中排除某些操作;
limit:截断流,使其元素不超过给定对象
skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
2.1 limit举例
需求,从Person列表中取出两个女性。

1 personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
输出结果为:

Person(name=欧阳雪, age=18, country=中国, sex=F)
Person(name=Harley, age=22, country=英国, sex=F)
2.2 skip举例
从Person列表中从第2个女性开始,取出所有的女性。

1 personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);
输出结果为:

Person(name=Harley, age=22, country=英国, sex=F)
Person(name=小梅, age=20, country=中国, sex=F)
Person(name=何雪, age=21, country=中国, sex=F)
2.3 distinct举例
1 personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);
输出结果为:

Person(name=Tom, age=24, country=美国, sex=M)
Person(name=向天笑, age=20, country=中国, sex=M)
Person(name=李康, age=22, country=中国, sex=M)
男性中有两个李康,去除掉了一个重复的。

3、Stream中间操作--映射
map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3.1 map举例
例1:比如,我们用一个PersonCountry类来接收所有的国家信息:

复制代码
1 @Data
2 class PersonCountry {
3 private String country;
4 }
5
6
7 personList.stream().map((p) -> {
8 PersonCountry personName = new PersonCountry();
9 personName.setCountry(p.getCountry());
10 return personName;
11 }).distinct().forEach(System.out::println);
复制代码
输出结果为:

PersonName(country=中国)
PersonName(country=美国)
PersonName(country=英国)
例2:假如有一个字符列表,需要提出每一个字符

1 List list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
代码如下:

根据字符串获取字符方法:

复制代码
1 public static Stream getCharacterByString(String str) {
2
3 List characterList = new ArrayList<>();
4
5 for (Character character : str.toCharArray()) {
6 characterList.add(character);
7 }
8
9 return characterList.stream();
10 }
复制代码
1 List list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
2
3 final Stream> streamStream
4 = list.stream().map(TestStreamAPI::getCharacterByString);
5 streamStream.forEach(System.out::println);
运行结果:

java.util.stream.ReferencePipeline$Head@3f91beef
java.util.stream.ReferencePipeline$Head@1a6c5a9e
java.util.stream.ReferencePipeline$Head@37bba400
java.util.stream.ReferencePipeline$Head@179d3b25
java.util.stream.ReferencePipeline$Head@254989ff
从输出结果及返回结果类型(Stream>)可以看出这是一个流中流,要想打印出我们想要的结果,需要对流中的每个流进行打印:

streamStream.forEach(sm -> sm.forEach(System.out::print));
运行结果为:

aaabbbcccdddddd
但我们希望的是返回的是一个流,而不是一个包含了多个流的流,而flatMap可以帮助我们做到这一点。

3.2 flatMap举例
改写上面的方法,将map改成flatMap:

1 final Stream characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
2 characterStream.forEach(System.out::print);
运行结果为:

aaabbbcccdddddd
3.3 map和flatMap的图解
map图解:

map在接收到流后,直接将Stream放入到一个Stream中,最终整体返回一个包含了多个Stream的Stream。

flatMap图解:

flatMap在接收到Stream后,会将接收到的Stream中的每个元素取出来放入一个Stream中,最后将一个包含多个元素的Stream返回。

ps:图画得丑,将就一下。

4、Stream中间操作--排序
sorted()--自然排序(Comparable)
sorted(Comparator com)--定制排序(Comparator)
自然排序比较好理解,这里只讲一下定制排序,对前面的personList按年龄从小到大排序,年龄相同,则再按姓名排序:

复制代码
1 final Stream sorted = personList.stream().sorted((p1, p2) -> {
2
3 if (p1.getAge().equals(p2.getAge())) {
4 return p1.getName().compareTo(p2.getName());
5 } else {
6 return p1.getAge().compareTo(p2.getAge());
7 }
8 });
9 sorted.forEach(System.out::println);
复制代码
运行结果:

复制代码
Person(name=欧阳雪, age=18, country=中国, sex=F)
Person(name=向天笑, age=20, country=中国, sex=M)
Person(name=小梅, age=20, country=中国, sex=F)
Person(name=何雪, age=21, country=中国, sex=F)
Person(name=Harley, age=22, country=英国, sex=F)
Person(name=李康, age=22, country=中国, sex=M)
Person(name=李康, age=22, country=中国, sex=M)
Person(name=Tom, age=24, country=美国, sex=M)
复制代码
5、终止操作--查找与匹配
allMatch--检查是否匹配所有元素
anyMatch--检查是否至少匹配一个元素
noneMatch--检查是否没有匹配所有元素
findFirst--返回第一个元素
findAny--返回当前流中的任意元素
count--返回流中元素的总个数
max--返回流中最大值
min--返回流中最小值
这些方面在Stream类中都有说明,这里不一一举例,只对allMatch、max各举一例进行说明。

4.1 allMatch
判断personList中的人是否都是成年人:

1 final boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
2 System.out.println("是否都是成年人:" + adult);
3
4 final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
5 System.out.println("是否都是中国人:" + chinaese);
运行结果:

是否都是成年人:true
是否都是中国人:false
4.1 max minx
1 final Optional maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
2 System.out.println("年龄最大的人信息:" + maxAge.get());
3
4 final Optional minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
5 System.out.println("年龄最小的人信息:" + minAge.get());
运行结果:

年龄最大的人信息:Person(name=Tom, age=24, country=美国, sex=M)
年龄最小的人信息:Person(name=欧阳雪, age=18, country=中国, sex=F)
5、归约
Stream API的归约操作可以将流中元素反复结合起来,得到一个值,有:

复制代码
1 Optional reduce(BinaryOperator accumulator);
2
3 T reduce(T identity, BinaryOperator accumulator);
4
5 U reduce(U identity,
6 BiFunction accumulator,
7 BinaryOperator combiner);
复制代码
5.1 求一个1到100的和
1 List integerList = new ArrayList<>(100);
2 for(int i = 1;i <= 100;i++) {
3 integerList.add(i);
4 }
5 final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
6 System.out.println("结果为:" + reduce);
结果为:5050
这个例子用到了reduce第二个方法:T reduce(T identity, BinaryOperator accumulator)

把这个动作拆解一下,其运算步骤模拟如下:

0 (1,2) -> 1 + 2 + 0
3 (3,4) -> 3 + 4 + 3
10 (5,6) -> 5 + 6 + 10
.
.
.
其运算步骤是,每次将列表的两个元素相加,并将结果与前一次的两个元素的相加结果进行累加,因此,在开始时,将identity设为0,因为第1个元素和第2个元素在相加的时候,前面还没有元素操作过。

5.2 求所有人的年龄之和
1 final Optional reduce = personList.stream().map(Person::getAge).reduce(Integer::sum);
2System.out.println("年龄总和:" + reduce);
年龄总和:169
6、收集
collect:将流转换为其他形式,接收一个Collector接口实现 ,用于给Stream中汇总的方法

1 R collect(Collector<? super T, A, R> collector);
2
3 R collect(Supplier supplier,
4 BiConsumer accumulator,
5 BiConsumer combiner);
collect不光可以将流转换成其他集合等形式,还可以进行归约等操作,具体实现也很简单,主要是与Collectors类搭配使用。

6.1 改写3.1 map举例中的的例子,将国家收集起来转换成List
1 final List collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
2 System.out.println(collect);
输出结果:

[中国, 美国, 英国]
6.2 计算出平均年龄
1 final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
2 System.out.println("平均年龄为:" + collect1);
输出结果:

平均年龄为:21.125
6.3 找出最小年龄、最大年龄
1 final Optional maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
2 System.out.println(maxAge2.get());
最小年龄类型。

还有其他很操作,可以参考java.util.stream.Collectors。

7 注意流的关闭
1 try(final Stream integerStream = personList.stream().map(Person::getAge)) {
2 final Optional minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
3 System.out.println(minAge.get());
4}
最好将流的操作放到try-with-resources,本章前面内容为了方便,没有放到try-with-resources中。

8 完整测试代码
复制代码
1 import lombok.Data;
2 import java.util.ArrayList;
3 import java.util.Arrays;
4 import java.util.List;
5 import java.util.Optional;
6 import java.util.stream.Collectors;
7 import java.util.stream.Stream;
8
9 public class TestStreamAPI {
10
11 public static void main(String[] args) {
12 List personList = new ArrayList<>();
13 personList.add(new Person("欧阳雪",18,"中国",'F'));
14 personList.add(new Person("Tom",24,"美国",'M'));
15 personList.add(new Person("Harley",22,"英国",'F'));
16 personList.add(new Person("向天笑",20,"中国",'M'));
17 personList.add(new Person("李康",22,"中国",'M'));
18 personList.add(new Person("小梅",20,"中国",'F'));
19 personList.add(new Person("何雪",21,"中国",'F'));
20 personList.add(new Person("李康",22,"中国",'M'));
21
22 // 1)找到年龄大于18岁的人并输出;
23 personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);
24
25 System.out.println("-------------------------------------------");
26
27 // 2)找出所有中国人的数量
28 long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
29 System.out.println("中国人有:" + chinaPersonNum);
30
31 // limit
32 personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
33 System.out.println();
34 // skip
35 personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);
36
37 // distinct
38 personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);
39
40 // map
41 personList.stream().map((p) -> {
42 PersonCountry personName = new PersonCountry();
43 personName.setCountry(p.getCountry());
44 return personName;
45 }).distinct().forEach(System.out::println);
46
47 // map2
48 List list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
49
50 final Stream> streamStream
51 = list.stream().map(TestStreamAPI::getCharacterByString);
52 // streamStream.forEach(System.out::println);
53 streamStream.forEach(sm -> sm.forEach(System.out::print));
54
55 // flatMap
56 final Stream characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
57 characterStream.forEach(System.out::print);
58
59 // sort
60 final Stream sorted = personList.stream().sorted((p1, p2) -> {
61
62 if (p1.getAge().equals(p2.getAge())) {
63 return p1.getName().compareTo(p2.getName());
64 } else {
65 return p1.getAge().compareTo(p2.getAge());
66 }
67 });
68 sorted.forEach(System.out::println);
69
70 // allMatch
71 final Stream stream = personList.stream();
72 final boolean adult = stream.allMatch(p -> p.getAge() >= 18);
73 System.out.println("是否都是成年人:" + adult);
74
75 final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
76 System.out.println("是否都是中国人:" + chinaese);
77
78 // max min
79 final Optional maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
80 System.out.println("年龄最大的人信息:" + maxAge.get());
81
82 final Optional minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
83 System.out.println("年龄最小的人信息:" + minAge.get());
84
85 // reduce
86 List integerList = new ArrayList<>(100);
87 for(int i = 1;i <= 100;i++) {
88 integerList.add(i);
89 }
90 final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
91 System.out.println("结果为:" + reduce);
92
93 final Optional totalAge = personList.stream().map(Person::getAge).reduce(Integer::sum);
94 System.out.println("年龄总和:" + totalAge);
95
96 // collect
97 final List collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
98 System.out.println(collect);
99
100 final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
101 System.out.println("平均年龄为:" + collect1);
102
103 final Optional maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
104 System.out.println(maxAge2.get());
105
106 try(final Stream integerStream = personList.stream().map(Person::getAge)) {
107 final Optional minAge2 = integerStream.collect(Collectors.minBy(Integer::compareTo));
108 System.out.println(minAge2.get());
109 }
110 }
111
112 public static Stream getCharacterByString(String str) {
113
114 List characterList = new ArrayList<>();
115
116 for (Character character : str.toCharArray()) {
117 characterList.add(character);
118 }
119
120 return characterList.stream();
121 }
122 }
123
124 @Data
125 class PersonCountry {
126 private String country;
127 }
128
129 @Data
130 class Person {
131 private String name;
132 private Integer age;
133 private String country;
134 private char sex;
135
136 public Person(String name, Integer age, String country, char sex) {
137 this.name = name;
138 this.age = age;
139 this.country = country;
140 this.sex = sex;
141 }
142 }
复制代码
原文地址https://www.cnblogs.com/wuhenzhidu/p/10740091.html

相关文章
|
13天前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
44 11
|
10天前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
1月前
|
数据采集 JSON Java
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
|
1月前
|
数据采集 存储 Java
Java爬虫获取微店店铺所有商品API接口设计与实现
本文介绍如何使用Java设计并实现一个爬虫程序,以获取微店店铺的所有商品信息。通过HttpClient发送HTTP请求,Jsoup解析HTML页面,提取商品名称、价格、图片链接等数据,并将其存储到本地文件或数据库中。文中详细描述了爬虫的设计思路、代码实现及注意事项,包括反爬虫机制、数据合法性和性能优化。此方法可帮助商家了解竞争对手,为消费者提供更全面的商品比较。
|
1月前
|
数据采集 算法 Java
如何在Java爬虫中设置动态延迟以避免API限制
如何在Java爬虫中设置动态延迟以避免API限制
|
2月前
|
算法 Java 程序员
菜鸟之路Day06一一Java常用API
《菜鸟之路Day06——Java常用API》由blue编写,发布于2025年1月24日。本文详细介绍了Java中常用的API,包括JDK7的时间类(Date、SimpleDateFormat、Calendar)和JDK8新增的时间API(ZoneId、Instant、DateTimeFormatter等),以及包装类的使用。通过多个实例练习,如时间计算、字符串转整数、十进制转二进制等,帮助读者巩固所学内容,提升编程技能。文章强调了理论与实践结合的重要性,鼓励读者多做练习以提高学习效率。
94 28
|
1月前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
216 5
|
自然语言处理 算法 数据建模
ElasticSearch 查询与 Java API 实践(上)
ElasticSearch 查询与 Java API 实践
364 0
ElasticSearch 查询与 Java API 实践(上)
|
SQL 存储 JSON
ElasticSearch 查询与 Java API 实践(下)
ElasticSearch 查询与 Java API 实践
1005 0
ElasticSearch 查询与 Java API 实践(下)
|
存储 Java 数据建模
ElasticSearch 查询与 Java API 实践(中)
ElasticSearch 查询与 Java API 实践
348 0
ElasticSearch 查询与 Java API 实践(中)