内容简介:java 8已经发行好几年了,前段时间java 12也已经问世,但平时的工作中,很多项目的环境还停留在java1.7中。而且java8的很多新特性都是革命性的,比如各种集合的优化、lambda表达式等,所以我们还是要去了解java8的魅力。今天我们来学习java8的Stream,并不需要理论基础,直接可以上手去用。我接触stream的原因,是我要搞一个用户收入消费的数据分析。起初的统计筛选分组都是打算用sql语言直接从mysql里得到结果来展现的。但在操作中我们发现这样频繁地访问数据库,性能会受到很大的影响
背景
java 8已经发行好几年了,前段时间 java 12也已经问世,但平时的工作中,很多项目的环境还停留在java1.7中。而且java8的很多新特性都是革命性的,比如各种集合的优化、lambda表达式等,所以我们还是要去了解java8的魅力。
今天我们来学习java8的Stream,并不需要理论基础,直接可以上手去用。
我接触stream的原因,是我要搞一个用户收入消费的数据分析。起初的统计筛选分组都是打算用 sql 语言直接从 mysql 里得到结果来展现的。但在操作中我们发现这样频繁地访问数据库,性能会受到很大的影响,分析速度会很慢。所以我们希望能通过访问一次数据库就拿到所有数据,然后放到内存中去进行数据分析统计过滤。
接着,我看了stream的API,发现这就是我想要的。
一、Stream理解
在java中我们称Stream为『 流 』,我们经常会用流去对集合进行一些流水线的操作。stream就像工厂一样,只需要把集合、命令还有一些参数灌输到 流水线 中去,就可以加工成得出想要的结果。这样的流水线能大大简洁代码,减少操作。
二、Stream流程
原集合 —> 流 —> 各种操作(过滤、分组、统计) —> 终端操作
Stream流的操作流程一般都是这样的,先将集合转为流,然后经过各种操作,比如过滤、筛选、分组、计算。最后的终端操作,就是转化成我们想要的数据,这个数据的形式一般还是集合,有时也会按照需求输出count计数。下文会一一举例。
三、API功能举例
首先,定义一个用户对象,包含姓名、年龄、性别和籍贯四个成员变量:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Log4j
@Builder
public class User {
//姓名
private String name;
//年龄
private Integer age;
//性别
private Integer sex;
//所在省市
private String address;
}
这里用lombok简化了实体类的代码。
然后创建需要的 集合数据 ,也就是源数据:
//1.构建我们的list
List<user> list= Arrays.asList(
new User("钢铁侠",40,0,"华盛顿"),
new User("蜘蛛侠",20,0,"华盛顿"),
new User("赵丽颖",30,1,"湖北武汉市"),
new User("詹姆斯",35,0,"洛杉矶"),
new User("李世民",60,0,"山西省太原市"),
new User("蔡徐坤",20,1,"陕西西安市"),
new User("葫芦娃的爷爷",70,0,"山西省太原市")
);
3.1 过滤
1)创建流 stream() / parallelStream()
- stream() : 串行流
- parallelStream(): 并行流
2)filter 过滤(T-> boolean)
比如要过滤年龄在40岁以上的用户,就可以这样写:
List<user>filterList=list.stream().filter(user->user.getAge() >= 40) .collect(toList());
filter里面,->箭头后面跟着的是一个 boolean值 ,可以写任何的过滤条件,就相当于sql中where后面的东西,换句话说, 能用sql实现的功能这里都可以实现
打印结果:
3)distinct 去重
和sql中的distinct关键字很相似。为了看到效果,此处在原集合中加入一个重复的人,就选择钢铁侠吧,复联4钢铁侠不幸遇害,大家还是比较伤心的。
List<user> list= Arrays.asList(
new User("钢铁侠",40,0,"华盛顿"),
new User("钢铁侠",40,0,"华盛顿"),
new User("蜘蛛侠",20,0,"华盛顿"),
new User("赵丽颖",30,1,"湖北武汉市"),
new User("詹姆斯",35,0,"洛杉矶"),
new User("李世民",60,0,"山西省太原市"),
new User("蔡徐坤”,18,1,"陕西西安市"),
new User("葫芦娃的爷爷",70,0,"山西省太原市")
);
//distinct 去重
List<user> distinctList = filterList.stream().distinct()
.collect(toList());
打印结果:
4)sorted排序
如果流中的元素的类实现了 Comparable 接口,即有自己的 排序 规则,那么可以直接调用 sorted() 方法 对元素进行排序,如:
Comparator.comparingInt
反之, 需要调用 sorted((T, T) -> int) 实现 Comparator 接口。
//sorted()
List<user> sortedList = distinctList.stream().sorted(Comparator.comparingInt(User::getAge))
.collect(toList());
打印结果:
结果按照年龄从小到大进行排序。
5)limit() 返回前n个元素
如果想知道这里面年龄最小的是谁,可作如下操作:
//limit 返回前n个元素
List<user> limitList = sortedList.stream().limit(1)
.collect(toList());
6)skip()
与limit恰恰相反,skip的意思是跳过,也就是去除前n个元素。
打印结果:
果然,前两个人都被去除了,只剩下最老的葫芦娃爷爷。
3.2 映射
1)map(T->R)
map是将T类型的数据转为R类型的数据,比如我们想要设置一个新的list,存储用户所有的城市信息。
//map(T->R) List<string>cityList=list.stream().map(User::getAddress).distinct().collect(toList());
打印结果:
2)flatMap(T -> Stream
)
将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流。
//flatMap(T -> Stream<r>)
List<string> flatList = new ArrayList<>();
flatList.add("唱,跳");
flatList.add("rape,篮球,music");
flatList = flatList.stream().map(s -> s.split(",")).flatMap(Arrays::stream).collect(toList());
打印结果:
这里原集合中的数据由逗号分割,使用split进行拆分后,得到的是Stream
Arrays::stream
将Stream
3.3 查找
1)allMatch(T->boolean)
检测是否全部满足参数行为,假如这些用户是网吧上网的用户名单,那就需要检查是不是每个人都年满18周岁了。
boolean isAdult=list.stream().allMatch(user->user.getAge() >= 18);
打印结果:
-
true
2)anyMatch(T->boolean)
检测是否有任意元素满足给定的条件,比如,想知道同学名单里是否有女生。
//anyMatch(T -> boolean) 是否有任意一个元素满足给定的条件 boolean isGirl=list.stream().anyMatch(user->user.getSex() == 1);
打印结果:
-
true
说明集合中有女生存在。
3)noneMatch(T -> boolean)
流中是否有元素匹配给定的 T -> boolean 条件。
比如检测有没有来自巴黎的用户。
boolean isLSJ=list.stream().noneMatch(user->user.getAddress().contains("巴黎"));
打印结果:
-
true
打印true说明没有巴黎的用户。
4)findFirst( ):找到第一个元素
Optional<user>fristUser=list.stream().findFirst();
打印结果:
User(name=钢铁侠,age=40,sex=0,address=华盛顿)
5)findAny():找到任意一个元素
Optional<user>anyUser=list.stream().findAny();
打印结果:
User(name=钢铁侠,age=40,sex=0,address=华盛顿)
这里我们发现findAny返回的也总是第一个元素,那么为什么还要进行区分呢?因为在并行流 parallelStream() 中找到的确实是任意一个元素。
Optional<user>anyParallelUser=list.parallelStream().findAny();
打印结果 :
Optional[User(name=李世民,age=60,sex=0,address=山西省太原市)]
3.4 归纳计算
1)求用户的总人数
long count=list.stream().collect(Collectors.counting());
我们可以简写为:
long count=list.stream().count();
运行结果:
-
8
2)得到某一属性的最大最小值
// 求最大年龄 Optional<user> max = list.stream().collect(Collectors.maxBy( Comparator.comparing(User::getAge))); // 求最小年龄 Optional<user> min = list.stream().collect(Collectors.minBy( Comparator.comparing(User::getAge)));
运行结果:
3)求年龄总和是多少
// 求年龄总和 int totalAge=list.stream().collect(Collectors.summingInt(User::getAge));
运行结果:
-
313
我们经常会用BigDecimal来记录金钱,假设想得到BigDecimal的总和:
// 获得列表对象金额, 使用reduce聚合函数,实现累加器 BigDecimal sum = myList.stream() .map(User::getMoney) .reduce(BigDecimal.ZERO,BigDecimal::add);
4)求年龄平均值
//求年龄平均值 double avgAge = list.stream().collect( Collectors.averagingInt(User::getAge));
运行结果:
-
39.125
5)一次性得到元素的个数、总和、最大值、最小值
IntSummaryStatistics statistics=list.stream().collect( Collectors.summarizingInt(User::getAge));
运行结果:
6)字符串拼接
要将用户的姓名连成一个字符串并用逗号分割。
String names=list.stream().map(User::getName)
.collect(Collectors.joining(", "));
运行结果:
钢铁侠, 钢铁侠, 蜘蛛侠, 赵丽颖, 詹姆斯, 李世民, 蔡徐坤, 葫芦娃的爷爷
3.5 分组
在数据库操作中,我们经常通过GROUP BY关键字对查询到的数据进行分组,java8的流式处理也提供了分组的功能。使用 Collectors.groupingBy 来进行分组。
1)可以根据用户所在城市进行分组
Map<string>>cityMap=list.stream() .collect(Collectors.groupingBy(User::getAddress));
结果是一个map,key为不重复的城市名,value为属于该城市的用户列表。已经实现了分组。
2)二级分组,先根据城市分组再根据性别分组
Map<string>>> group = list.stream().collect(
Collectors.groupingBy(User::getAddress, // 一级分组,按所在地区
Collectors.groupingBy(User::getSex))); // 二级分组,按性别
运行结果:
3)如果仅仅想统计各城市的用户个数是多少,并不需要对应的list
按城市分组并统计人数:
Map<string>cityCountMap=list.stream() .collect(Collectors.groupingBy(User::getAddress,Collectors.counting()));
运行结果:
4)当然,也可以先进行过滤再分组并统计人数
Map<string>map=list.stream().filter(user->user.getAge() <= 30) .collect(Collectors.groupingBy(User::getAddress,Collectors.counting()));
运行结果:
5)partitioningBy 分区
分区与分组的区别在于,分区是按照 true 和 false 来分的,因此partitioningBy 接受的参数的 lambda 也是 T -> boolean
//根据年龄是否小于等于30来分区
Map<boolean>> part = list.stream()
.collect(partitioningBy(user -> user.getAge() <= 30));
运行结果:
总结
到目前为止,stream的功能我们已经用了很多了,感觉有点眼花缭乱却无所不能,stream能做的事情远远不止这些。
我们可以多学习使用stream,把原来复杂的sql查询,一遍又一遍地for循环的复杂代码重构,让代码更 简洁易懂,可读性强。
作者:杨亨
来源:宜信技术学院
以上所述就是小编给大家介绍的《简洁方便的集合处理——Java 8 stream流》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 方便的 Kubernetes API 参考文档
- 使用drawio进行画图真的很方便
- WebStorm 2019.2 发布,更智能方便
- Prometheus 监控 MongoDB 真的太方便了
- HTTPS 不方便的地方!你知道多少?。。。
- 云原生时代如何方便的进行本地调试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Chinese Authoritarianism in the Information Age
Routledge / 2018-2-13 / GBP 115.00
This book examines information and public opinion control by the authoritarian state in response to popular access to information and upgraded political communication channels among the citizens in co......一起来看看 《Chinese Authoritarianism in the Information Age》 这本书的介绍吧!