乐字节-Java8新特性-接口默认方法之Stream流(下)
接上一篇:《Java8新特性之stream》,下面继续接着讲Stream
5、流的中间操作
常见的流的中间操作,归为以下三大类:筛选和切片流操作、元素映射操作、元素排序操作:
| 操作 | 描述 |
|---|---|
| 筛选和切片 | filter(T -> boolean):保留 boolean 为 true 的元素 limit(long n):返回前 n 个元素 skip(long n):去除前 n 个元素 distinct():去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的 |
| 映射 | map(T -> R):将流中的每一个元素 T 映射为 R(类似类型转换) flatMap(T -> Stream<R>): 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流 |
| 排序 | sorted() / sorted((T, T) -> int):如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,如 Stream<Integer> |
5.1、筛选和切片
例如以订单数据为例,在做报表展示时,会根据订单状态、用户信息、支付结果等状态来分别展示(即过滤和统计展示)
定义订单Order类
public class Order {
// 订单id
private Integer id;
// 订单用户id
private Integer userId;
// 订单编号
private String orderNo;
// 订单日期
private Date orderDate;
// 收货地址
private String address;
// 创建时间
private Date createDate;
// 更新时间
private Date updateDate;
// 订单状态 0-未支付 1-已支付 2-代发货 3-已发货 4-已接收 5-已完成
private Integer status;
// 是否有效 1-有效订单 0-无效订单
private Integer isValid;
//订单总金额
private Double total;
/**
此处省略getter/setter方法
*/
}
测试
public static void main(String[] args) {
Order order01 = new Order(1,10,"20190301",
new Date(),"上海市-浦东区",new Date(),new Date(),4,1,100.0);
Order order02 = new Order(2,30,"20190302",
new Date(),"北京市四惠区",new Date(),new Date(),1,1,2000.0);
Order order03 = new Order(3,20,"20190303",
new Date(),"北京市-朝阳区",new Date(),new Date(),4,1,500.0);
Order order04 = new Order(4,40,"20190304",
new Date(),"北京市-大兴区",new Date(),new Date(),4,0,256.0);
Order order05 = new Order(5,40,"20190304",
new Date(),"上海市-松江区",new Date(),new Date(),4,0,1000.0);
List<Order> ordersList= Arrays.asList(order01,order02,order03,order04);
// 过滤订单集合 有效订单 并打印到控制台
ordersList.stream().filter((order)->order.getIsValid()==1).forEach(System.out::println);
// 过滤订单集合有效订单 取前两条有效订单 并打印到控制台
ordersList.stream().filter((order)->order.getIsValid()==1).limit(2).forEach(System.out::println);
}
// 过滤订单集合有效订单 取最后一条记录
ordersList.stream().filter((order)->order.getIsValid()==1)
.skip(ordersList.size()-2).forEach(System.out::println);
// 去除订单编号重复的无效订单记录 此时因为比较的为Object Order对象需要重写HashCode 与Equals 方法
/**
* 重写 equals 方法
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
boolean flag = false;
if (obj == null) {
return flag;
}
Order order = (Order) obj;
if (this == order) {
return true;
} else {
return (this.orderNo.equals(order.orderNo));
}
}
/**
* 重写hashcode方法
* @return
*/
@Override
public int hashCode() {
int hashno = 7;
hashno = 13 * hashno + (orderNo == null ? 0 : orderNo.hashCode());
return hashno;
}
// 过滤订单集合无效订单 去除订单号重复记录
ordersList.stream().filter((order)->order.getIsValid()==0).distinct().forEach(System.out::println);
5.2、映射
//过滤订单集合有效订单 获取所有订单订单编号
ordersList.stream().filter((order)->order.getIsValid()==1).map((order)->order.getOrderNo()).forEach(System.out::println);
// 过滤有效订单 并分离每个订单下收货地址市区信息
ordersList.stream().map(o->o.getAddress().split("-")).flatMap(Arrays::stream).forEach(System.out::println);
5.3、排序
//过滤有效订单 并根据用户id 进行排序
ordersList.stream().filter((order)->order.getIsValid()==1)
.sorted((o1,o2)->o1.getUserId()-o2.getUserId()).forEach(System.out::println);
//或者等价写法
ordersList.stream().filter((order)->order.getIsValid()==1)
.sorted(Comparator.comparingInt(Order::getUserId)).forEach(System.out::println);
// 定制排序规则
/*过滤有效订单
* 定制排序:如果订单状态相同 根据订单创建时间排序 反之根据订单状态排序
*/
ordersList.stream().filter((order)->order.getIsValid()==1).sorted((o1,o2)->{
if(o1.getStatus().equals(o2.getStatus())){
return o1.getCreateDate().compareTo(o2.getCreateDate());
}else{
return o1.getStatus().compareTo(o2.getStatus());
}}).forEach(System.out::println);
6、流的终止操作
终止操作会从流的流水线生成结果。其结果是任何不是流的值,比如常见的List、 Integer,甚 至void等结果。 对于流的终止操作,分为以下三类:
| 操作 | 描述 |
|---|---|
| 查找与匹配 | allMatch:检查是否匹配所有元素 anyMatch(T -> boolean): 流中是否有一个元素匹配给定的 T -> boolean 条件 noneMatch(T -> boolean): 流中是否没有元素匹配给定的 T -> boolean 条件 findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素) findFirst():找到第一个元素 max():返回流中最大值 min():返回流中最小值 count():返回流中元素的总个数 |
| 归约: 将流中元素反复结合起来,得到一个值 | reduce((T, T) -> T) 和 reduce(T, (T, T) -> T): 用于组合流中的元素,如求和,求积,求最大值等 |
| 收集: 将流转换为其他形式,接收一个Collertor接口的实现,用于给Stream中元素做汇总的方法 | collect() |
6.1、查找与匹配
// 筛选所有有效订单 匹配用户id =20 的所有订单
System.out.println("allMatch匹配结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).allMatch((o) -> o.getUserId() == 20));
System.out.println("anyMatch匹配结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).anyMatch((o) -> o.getUserId() == 20));
System.out.println("noneMatch匹配结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).noneMatch((o) -> o.getUserId() == 20));
// 筛选所有有效订单 返回订单总数
System.out.println("count结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).count());
// 筛选所有有效订单 返回金额最大订单值
Optional<Double> max=ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).max(Double::compare);
System.out.println("订单金额最大值:"+max.get());
// 筛选所有有效订单 返回金额最小订单值
Optional<Double> min=ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).min(Double::compare);
System.out.println("订单金额最小值:"+min.get());
6.2、归约
将流中元素反复结合起来,得到一个值的操作。
// 归约操作 计算有效订单总金额
System.out.println("有效订单总金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1).map(Order::getTotal).reduce(Double::sum).get());
6.3、Collector收集数据
6.3.1、收集
将流转换为其他形式,coollect 方法作为终端操作, 接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。最常用的方法,把流中所有元素收集到一个 List, Set 或 Collection 中
toList
toSet
toCollection
toMap
// 收集操作
// 筛选所有有效订单 并收集订单列表
List<Order> orders= ordersList.stream().filter((order) -> order.getIsValid() == 1).collect(Collectors.toList());
orders.forEach(System.out::println);
// 筛选所有有效订单 并收集订单号 与 订单金额
Map<String,Double> map=ordersList.stream().filter((order) -> order.getIsValid() == 1).
collect(Collectors.toMap(Order::getOrderNo, Order::getTotal));
// java8 下对map 进行遍历操作 如果 Map 的 Key 重复了,会报错
map.forEach((k,v)->{
System.out.println("k:"+k+":v:"+v);
});
6.3.2、汇总
countintg():用于计算总和
count():用于计算总和(推荐使用,写法更简洁)
summingInt() ,summingLong(),summingDouble():用于计算总和
averagingInt(),averagingLong(),averagingDouble()用于平均
summarizingInt,summarizingLong,summarizingDouble 同样可以实现计算总和,平均等操作,比如summarizingInt 结果会返回IntSummaryStatistics 类型 ,然后通过get方法获取对应汇总值即可
// 汇总操作
//筛选所有有效订单 返回订单总数
System.out.println("count结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).collect(Collectors.counting()));
System.out.println("count结果:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).count());
// 返回订单总金额
System.out.println("订单总金额:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).collect(Collectors.summarizingDouble(Order::getTotal)));
System.out.println("订单总金额:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).mapToDouble(Order::getTotal).sum());
System.out.println("订单总金额:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).map(Order::getTotal).reduce(Double::sum).get());
// 返回 用户id=20 有效订单平均每笔消息金额
System.out.println("用户id=20 有效订单平均每笔消费金额:"+ordersList.stream().
filter((order) -> order.getIsValid() == 1).
filter((order -> order.getUserId()==20))
.collect(Collectors.averagingDouble(Order::getTotal)));
System.out.println("用户id=20 有效订单平均每笔消费金额:"+
ordersList.stream().
filter((order) -> order.getIsValid() == 1).
filter((order -> order.getUserId()==20))
.mapToDouble(Order::getTotal).average().getAsDouble());
System.out.println("用户id=20 有效订单平均每笔消费金额:"+
ordersList.stream().
filter((order) -> order.getIsValid() == 1).
filter((order -> order.getUserId()==20))
.collect(Collectors.summarizingDouble(Order::getTotal)).getAverage());
// 筛选所有有效订单 并计算订单总金额
System.out.println("订单总金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)
.collect(Collectors.summingDouble(Order::getTotal)));
// 筛选所有有效订单 并计算最小订单金额
System.out.println("最小订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).collect(Collectors.minBy(Double::compare)));
// 筛选所有有效订单 并计算最大订单金额
System.out.println("最大订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).collect(Collectors.maxBy(Double::compare)));
6.3.3、最值
maxBy,minBy 两个方法,需要一个 Comparator 接口作为参数,实现最大 最小值获取操作
// 取最会
// 筛选所有有效订单 并计算最小订单金额
System.out.println("最小订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).collect(Collectors.minBy(Double::compare)));
// 筛选所有有效订单 并计算最大订单金额
System.out.println("最大订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map(Order::getTotal).collect(Collectors.maxBy(Double::compare)));
6.3.4、分组
groupingBy 用于将数据分组,最终返回一个 Map 类型
groupingBy 可以接受一个第二参数实现多级分组
// 分组-根据有效订单支付状态进行分组操作
Map<Integer,List<Order>> g01=ordersList.stream().filter((order) -> order.getIsValid() == 1)
.collect(Collectors.groupingBy(Order::getStatus));
g01.forEach((status,order)->{
System.out.println("----------------");
System.out.println("订单状态:"+status);
order.forEach(System.out::println);
});
// 分组-查询有效订单 根据用户id 和 支付状态进行分组
Map<Integer,Map<String,List<Order>>> g02= ordersList.stream().filter((order) -> order.getIsValid() == 1)
.collect(Collectors.groupingBy(Order::getUserId,Collectors.groupingBy((o)->{
if(o.getStatus()==0){
return "未支付";
}else if (o.getStatus()==1){
return "已支付";
}else if (o.getStatus()==2){
return "待发货";
}else if (o.getStatus()==3){
return "已发货";
}else if (o.getStatus()==4){
return "已接收";
} else{
return "已完成";
}
})));
g02.forEach((userId,m)->{
System.out.println("用户id:"+userId+"-->有效订单如下:");
m.forEach((status,os)->{
System.out.println("状态:"+status+"---订单列表如下:");
os.forEach(System.out::println);
});
System.out.println("-----------------------");
});
6.3.5、partitioningBy 分区
分区与分组的区别在于,分区是按照 true 和 false 来分的,因此partitioningBy 接受的参数的 lambda 也是 T -> boolean
// 分区操作 筛选订单金额>1000 的有效订单
Map<Boolean,List<Order>> g03= ordersList.stream().filter((order) -> order.getIsValid() == 1)
.collect(Collectors.partitioningBy((o)->o.getTotal()>1000));
g03.forEach((b,os)->{
System.out.println("分区结果:"+b+"--列表结果:");
os.forEach(System.out::println);
});
// 拼接操作 筛选有效订单 并进行拼接
String orderStr=ordersList.stream().filter((order) -> order.getIsValid() == 1).map(Order::getOrderNo)
.collect(Collectors.joining(","));
System.out.println(orderStr);
乐字节-Java新特性之stream流就介绍到这里了,接下来小乐还会接着给大家讲解Java8新特性之Optional,欢迎关注,转载请说明出处和作者。
乐字节-Java8新特性-接口默认方法之Stream流(下)的更多相关文章
- 乐字节-Java8新特性-接口默认方法
总概 JAVA8 已经发布很久,而且毫无疑问,java8是自java5(2004年发布)之后的最重要的版本.其中包括语言.编译器.库.工具和JVM等诸多方面的新特性. Java8 新特性列表如下: 接 ...
- Java8新特性——接口默认方法
Java 8 新增了接口的默认方法. 简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法. 我们只需在方法名前面加个default关键字即可实现默认方法. 为什么要有这个特性? 首先 ...
- 乐字节-Java8新特性-Lambda表达式
上一篇文章我们了解了Java8新特性-接口默认方法,接下来我们聊一聊Java8新特性之Lambda表达式. Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作 ...
- 乐字节-Java8新特性之Base64和重复注解与类型注解
上一篇小乐给大家说了<乐字节-Java8新特性之Date API>,接下来小乐继续给大家说一说Java8新特性之Base64和重复注解与类型注解. 一.Base64 在Java 8中,内置 ...
- Java8新特性系列-默认方法
Java8 Interface Default and Static Methods 原文连接:Java8新特性系列-默认方法 – 微爱博客 在 Java 8 之前,接口只能有公共抽象方法. 如果不强 ...
- 乐字节-Java8新特性之方法引用
上一篇小乐介绍了<Java8新特性-函数式接口>,大家可以点击回顾.这篇文章将接着介绍Java8新特性之方法引用. Java8 中引入方法引用新特性,用于简化应用对象方法的调用, 方法引用 ...
- 乐字节-Java8新特性之函数式接口
上一篇小乐带大家学过 Java8新特性-Lambda表达式,那什么时候可以使用Lambda?通常Lambda表达式是用在函数式接口上使用的.从Java8开始引入了函数式接口,其说明比较简单:函数式接口 ...
- 乐字节-Java8新特性之Optional
上一篇小乐带大家了解了Java新特性之Stream,接下来将会继续述说Java新特性之Optional Optional<T>类(java.util.Optional)是一个容器类,代表一 ...
- 乐字节-Java8新特性之Date API
上一篇文章,小乐给大家带来了Java8新特性之Optional,接下来本文将会给大家介绍Java8新特性之Date API 前言: Java 8通过发布新的Date-Time API来进一步加强对日期 ...
随机推荐
- c#依参数自动生成控件
很多系统都带有自定义报表的功能,而此功能都需依参数自动生成控件,举例如下: 如上图,一条查询语句当中,包含了3个参数,其中两个是日期型(使用:DATE!进行标识),一个是字符型(使用:进行标识),要生 ...
- WPF设计时
资料太少.中文没有.英文的也残缺不全.待补充.问题暂时解决. 设计器通过使用命名约定来发现自定义设计时程序集 运行时程序集与设计时程序集对应关系 加载顺序 程序集名称(*表示版本号,可省略) 0 ...
- ionic 2.x 3.x input触发调用键盘搜索及事件
html (1.input type='search' 2.将input套在一个带action的form中 ) <form action=""> <ion- ...
- Google guava cache源码解析1--构建缓存器(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. CacheBuilder-->maximumSize(long size) /** ...
- DS-博客作业03--栈和队列
1.本周学习总结 第三章主要介绍栈和队列的基本概念,存储结构,基本运算算法设计和应用实例.从组成元素的逻辑关系来看,栈和队列都属于线性结构.栈和队列与线性表的不同之处就在于他们的相关运算具有一些特殊性 ...
- 11_python_闭包迭代器
一.函数名(第一类对象) 函数名是一个变量,但它是一个特殊的变量,与括号配合可以执行函数的变量. def func(): print("呵呵") print(func) 结果: & ...
- python之ETL数据清洗案例源代码
#python语言 import pandas as pd import time data = pd.read_excel('ETL_数据清洗挑战.xlsx','测试数据',dtype=str)#读 ...
- Liferay7 BPM门户开发之2: BPMN 2.0 规范入门 (Activiti BPMN extensions)
Liferay最大的问题是BPM弱,如果做企业开发,BPM必不可少,所以直入主题,做个BPMN2入门. 本文参考地址:http://activiti.org/userguide/index.html# ...
- 【Canal源码分析】整体架构
本文详解canal的整体架构. 一.整体架构 说明: server代表一个canal运行实例,对应于一个jvm instance对应于一个数据队列 (1个server对应1..n个instance) ...
- 全网最详细的用pip安装****模块报错:Could not find a version that satisfies the requirement ****(from version:) No matching distribution found for ****的解决办法(图文详解)
不多说,直接上干货! 问题详情 这个问题,很普遍.如我这里想实现,Windows下Anaconda2 / Anaconda3里正确下载安装用来向微信好友发送消息的itchat库. 见,我撰写的 全网最 ...