Lambda--Optional、Collectors高级进阶方法
Lambda--Collectors、optional高级使用
偶然看到了同事groupingBy用法,然后百度衍生出了optional,collectors,map等各种用法。突然发现自己之前写的代码又烂又复杂了,后面用optional可以防止空指针,collectors也可以极大简化代码量。
@
- 1.Optional
- 2.Collectors
- 2.1.averagingDouble--计算平均值
- 2.2.collectingAndThen--转换类型
- 2.3.counting--类似count()
- 2.4.groupingBy*、groupingByConcurrent--分组(重点)
- 2.5.partitioningBy--分区
- 2.6.joining--拼接元素
- 2.7.mapping--类似map
- 2.8.maxBy、minBy--类型max(),min()
- 2.9.reducing--类型reduce()
- 2.10.summarizingDouble*--汇总max,min,count,sum,avg(妙点)
- 2.11.toCollection--转换成集合
- 2.12.toMap--转换成list
- 3.总结
- 4.补充
1.Optional
optional里面有防止空指针的语句,但我觉得有些时候报空指针错更方便定位到问题。比如从库里查找出数据,没找到但是你初始化了一个默认值做了后续处理并返回给前端展示了,这其实就算是BUG了,当然有些时候避免空指针报错也是非常不错的选择。
1.1.Optional--基本方法
//of会报空指针,ofNullable不会(可以防止空指针),而是重新new个optional
Optional.of()
Optional.ofNullable
//user不为空即输出
Optional.ofNullable(user).ifPresent(System.out::println);
//user为空则重新new一个
Optional.ofNullable(user).orElse(User.builder().build());
//user为空则报空指针错误(虽然ofNullable里面new了optional,但user依然会为空)
Optional.ofNullable(user).orElseThrow(() -> new NullPointerException());
//user为空则重新new一个
Optional.ofNullable(user).orElseGet(() -> User.builder().build());
//初始化list数组
List<String> list = null;
List<String> newList = Optional.ofNullable(list).orElse(Lists.newArrayList());
1.2.Optional--filter、map
Optional中可以像stream流一样操作数据
/**
* @program: springboot
* @description:
* @author: huyuqiao
* @create: 2021/08/07 10:57
*/
@Data
@Builder(toBuilder = true)
public class UserInfo {
private String name;
private String email;
private Integer price;
}
@Test
public void optionalTest(){
// Optional--操作对象:filter
UserInfo userInfo = UserInfo.builder()
.name("huyuqiao")
.email("1842449680@qq.com")
.price(100)
.build();
UserInfo newUserInfo = Optional.ofNullable(userInfo).filter(u -> u.getPrice() < 1000).orElse(UserInfo.builder().build());
System.out.println(newUserInfo);
// Optional--操作对象:map
String name = Optional.ofNullable(userInfo).map(UserInfo::getName).orElse("自定义姓名");
System.out.println(name);
// Optional--操作对象:flatmap
Optional.ofNullable(userInfo).flatMap(u -> Optional.ofNullable(u.getName()));
}
1.2.Optional--flatMap
flatMap:扁平化获取层级下的元素并组装成同一层级。
比如现有数组[[1],[2],[3]],要提取里面所有元素并组装成[1,2,3],可利用flatMap获取
@Test
public void flatMapTest(){
List<List<Integer>> outer = new ArrayList<>();
List<Integer> inner1 = new ArrayList<>();
inner1.add(1);
inner1.add(9);
List<Integer> inner2 = new ArrayList<>();
inner2.add(2);
List<Integer> inner3 = new ArrayList<>();
inner3.add(3);
List<Integer> inner4 = new ArrayList<>();
inner4.add(4);
List<Integer> inner5 = new ArrayList<>();
inner5.add(5);
outer.add(inner1);
outer.add(inner2);
outer.add(inner3);
outer.add(inner4);
outer.add(inner5);
List<Integer> result = outer.stream().flatMap(inner -> inner.stream().map(i -> i + 1)).collect(Collectors.toList());
System.out.println(result);
}
//[2, 10, 3, 4, 5, 6]
2.Collectors
会使用了optional基本语句后,后面就可以开始使用Collectors语句了,个人感觉对开发非常实用,可以极大简化代码开发量。先给出基本类,然后就可以使用了
/**
* @program: springboot
* @description:
* @author: huyuqiao
* @create: 2021/08/06 10:10
*/
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 学生信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
/** 姓名 */
private String name;
/** 总分 */
private int totalScore;
/** 是否本地人 */
private boolean local;
/** 年级 */
private GradeType gradeType;
/**
* 年级类型
*/
public enum GradeType {ONE,TWO,THREE}
}
2.1.averagingDouble--计算平均值
注意: double格式可以表示-253到253范围内的所有连续整数。如果管道有超过253的值,则平均计算中的除数将在253处饱和,从而导致额外的数值误差。(所以计算数多时需要专用BigDecimal进行计算)
//统计所有学生的平均总成绩
@Test
public void averagingDoubleTest(){
Double avgScore = menu.stream().collect(Collectors.averagingDouble(Student::getTotalScore));
Optional.ofNullable(avgScore).ifPresent(System.out::println);
}
2.2.collectingAndThen--转换类型
collectingAndThen方法:调整Collector收集器以执行其它的结束转换
@Test
public void collectingAndThenTest(){
// unmodifiablelist:不能add,set。arrays.aslist可以set,但是不能add
List<Student> studentList = menu.stream().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
System.out.println(studentList);
//collectingandthen:计算总平均值并转换成自定义字符串返回
Optional.ofNullable(menu.stream().collect(
Collectors.collectingAndThen(Collectors.averagingInt(Student::getTotalScore), a -> "the average totalscore is" + a)
)).ifPresent(System.out::println);
}
2.3.counting--类似count()
counting方法:统计collectors收集器元素
@Test
public void countingTest(){
Optional.of(menu.stream().collect(Collectors.counting())).ifPresent(System.out::println);
}
// 7
2.4.groupingBy*、groupingByConcurrent--分组(重点)
groupingBy方法:分组并返回到map中(个人感觉非常好用,对商品进行分组返回给前端,大大简化了代码量)。
groupingByConcurrent:并发分组,无法保证返回数据类型的顺序或线程安全性
//按对象某一属性进行分组
@Test
public void groupByTest(){
Map<Student.GradeType, List<Student>> collect = menu.stream().collect(Collectors.groupingBy(Student::getGradeType));
Optional.ofNullable(collect).ifPresent(System.out::println);
//{THREE=[Student(name=刘一, totalScore=721, local=true, gradeType=THREE), Student(name=陈二, totalScore=637, local=true, gradeType=THREE), Student(name=张三, totalScore=666, local=true, gradeType=THREE), Student(name=王五, totalScore=483, local=false, gradeType=THREE), Student(name=赵六, totalScore=367, local=true, gradeType=THREE)], TWO=[Student(name=李四, totalScore=531, local=true, gradeType=TWO)], ONE=[Student(name=孙七, totalScore=499, local=false, gradeType=ONE)]}
//分组后统计每组的平均分数值,并对每组key升序排列
Map<Student.GradeType, Double> map = menu.stream().collect(Collectors.groupingBy(Student::getGradeType, TreeMap::new , Collectors.averagingInt(Student::getTotalScore)));
Optional.ofNullable(map).ifPresent(System.out::println);
//{ONE=499.0, TWO=531.0, THREE=574.8}
}
GroupingBy其他功能:按单个属性、多个属性,条件分组、多级分组、分组后组装成其他集合
package com.empirefree.springboot;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* @program: springboot
* @description:
* @author: huyuqiao
* @create: 2021/08/05 09:47
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class CollectorsTest {
@Data
public class Product {
private final Long id;
private final Integer num;
private final BigDecimal price;
private final String name;
private final String category;
}
@Test
public void testanything(){
// 参考文章:https://blog.csdn.net/u014231523/article/details/102535902
Product prod1 = new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");
Product prod1_1 = new Product(11L, 11, new BigDecimal("15.5"), "面包", "零食");
Product prod2 = new Product(2L, 2, new BigDecimal("20"), "饼干", "零食");
Product prod3 = new Product(3L, 3, new BigDecimal("30"), "月饼", "零食");
Product prod4 = new Product(4L, 3, new BigDecimal("10"), "青岛啤酒", "啤酒");
Product prod5 = new Product(5L, 10, new BigDecimal("15"), "百威啤酒", "啤酒");
List<Product> prodList = Arrays.asList(prod1, prod1_1, prod2, prod3, prod4, prod5);
Object object7 = prodList.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get));
System.out.println(object7);
// 1、按某个属性分组
Map<String, List<Product>> productMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory));
System.out.println(JSON.toJSONString(productMap));
// 2、按多个属性分组
Map<String, List<Product>> productMap2 = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));
System.out.println(JSON.toJSONString(productMap2));
// 3、按条件分组
Map<String, List<Product>> productMap3 = prodList.stream().collect(Collectors.groupingBy(item ->{
if (item.getNum() < 3){
return "3";
} else {
return "other";
}
}));
System.out.println(JSON.toJSONString(productMap3));
// 4、多级分组
Map<String, Map<String, List<Product>>> productMap4 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.groupingBy(item ->{
if (item.getNum() < 3){
return "3";
} else {
return "other";
}
})));
System.out.println(JSON.toJSONString(productMap4));
// 5、分组求总数、求和
Map<String, Long> productMap5 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
Map<String, Integer> productMap6 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
System.out.println(JSON.toJSONString(productMap5));
System.out.println(JSON.toJSONString(productMap6));
// 6、分组后只获取最大值
Map<String, Product> prodMap7 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));
System.out.println(prodMap7);
// 7、分组后组装成其他集合
Map<String, Set<String>> productMap8 = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));
System.out.println(JSON.toJSONString(productMap8));
}
}
2.5.partitioningBy--分区
partitioningBy方法:分区计算。返回map的可序列化和线程安全性无法保证
partitioningBy:返回map的key是Boolean。无法保证线程安全性
groupingBy:返回map的key是分组的值。可以保证线程安全性
@Test
public void partitioningByTest(){
Map<Boolean, Double> collect = menu.stream().collect(Collectors.partitioningBy(Student::isLocal, Collectors.averagingInt(Student::getTotalScore)));
Optional.ofNullable(collect).ifPresent(System.out::println);
}
2.6.joining--拼接元素
joining方法:连接colletors的每个元素
@Test
public void joiningTest(){
Optional.ofNullable(menu.stream().map(Student::getName).collect(Collectors.joining(",", "[", "]"))).ifPresent(System.out::println);
//[刘一,陈二,张三,李四,王五,赵六,孙七]
}
2.7.mapping--类似map
mapping方法:类似于map()。
@Test
public void mappingTest(){
Optional.ofNullable(menu.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(",", "[", "]")))).ifPresent(System.out::println);
}
2.8.maxBy、minBy--类型max(),min()
maxBy、minBy方法:获取比较器最大和最小值。返回的是Optional
@Test
public void maxOrMinByTest(){
menu.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getTotalScore))).ifPresent(System.out::println);
menu.stream().collect(Collectors.minBy(Comparator.comparing(Student::getTotalScore))).ifPresent(System.out::println);
}
2.9.reducing--类型reduce()
reducing方法:对收集器元素计算
@Test
public void reducingTest(){
Integer result = menu.stream().map(Student::getTotalScore).collect(Collectors.reducing(0, (a, b) -> a + b));
System.out.println(result);
}
2.10.summarizingDouble*--汇总max,min,count,sum,avg(妙点)
summarizingDouble方法:汇总list中某属性的所有汇总值(最大、最小、总数,总和,平均值)
@Test
public void summarizingInt(){
DoubleSummaryStatistics result = menu.stream().collect(Collectors.summarizingDouble(Student::getTotalScore));
Optional.ofNullable(result).ifPresent(System.out::println);
}
2.11.toCollection--转换成集合
toCollection方法:转换收集器的集合类型
//转换成LinkedList
@Test
public void toCollectionTest(){
Optional.of(menu.stream().filter(s -> s.getTotalScore() > 600).collect(Collectors.toCollection(LinkedList::new))).ifPresent(s -> {
System.out.println(s.getClass());
System.out.println(s);
});
}
2.12.toMap--转换成list
toMap方法:可以将list转换成map(但要注意key值重复与value为空情况)
@Test
public void listToMapTest(){
// 1 list to map -- obj
List<User> users = Arrays.asList(
new User(101, "Jack"),
new User(102, "Kreas"),
new User(103, "Marry"),
// new User(103, "Marry2"),
new User(104, "Timi"),
new User(105, "Alice")
// new User(105, null)
);
// key不能有重复
Map<Long, User> map = users.stream().collect(Collectors.toMap(User::getId, o -> o));
System.out.println(JSON.toJSONString(map));
// 1.2 list to map -- 属性
Map<Long, String> map2 = users.stream().collect(Collectors.toMap(User::getId, User::getName));
System.out.println(JSON.toJSONString(map2));
// 3 list to map -- 解决key重复 value为空则报空指针
Map<Long, String> map3 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o1));
System.out.println(JSON.toJSONString(map3));
Map<Long, String> map4 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o2));
System.out.println(JSON.toJSONString(map4));
Map<Long, String> map5 = users.stream().collect(Collectors.toMap(User::getId, User::getName, (o1, o2) -> o1 + "," + o2));
System.out.println(JSON.toJSONString(map5));
// 4 list to map -- 解决value为空,但不能解决key值重复
Map<Long, String> map6 = users.stream().collect(Collectors.toMap(User::getId, o -> o.getName() == null ? "" : o.getName()));
System.out.println(JSON.toJSONString(map6));
}
注意:当有key重复以及value为null情况时,个人建议还是直接用put/putIfAbsent。
Map<Long, String> map7 = new HashMap<>();
users.stream().forEach(user -> map7.put(user.getId(), user.getName()));
System.out.println(map7);
3.总结
个人认为:
Optional.ofNullable(对象).orElse(对象.builder().build()):防止空指针非常实用
Collectors里面最重要的当属groupingBy与summarizingDouble。其他的个人觉得知道要能看得懂是干什么用的,毕竟很多都可以直接在stream里面直接处理好,没必要在collect里面处理。
另外:
map中重复值+1操作:computeIfAbsent可以实现
提取List成list<>:flatMap可以实现
List转map:map的put/putifAbsent即可
Collectors参考链接:https://blog.csdn.net/sl1992/article/details/98900343
4.补充
4.1.Map操作--compute计算
Map:判断list值,没有就添加,有就value+1
@Test
public void mapTest(){
/*
* compute:计算并更新值(但是可能会空指针)
computeIfAbsent:Value不存在时才计算
computeIfPresent:Value存在时才计算
* */
HashMap<String, Integer> prices = new HashMap<>();
// 往HashMap中添加映射关系
prices.put("Shoes", 180);
prices.put("Bag", 300);
prices.put(null, 150);
prices.put(null, 250);
System.out.println("HashMap: " + prices);
//有就不put,没有才put
prices.putIfAbsent("Shoes", 1810);
//有才put,没有不put
prices.computeIfPresent("Shoes3", (key, value) -> value + 100);
//有就不put
prices.computeIfAbsent("Shoes4", key -> 18120);
//无论有无都put计算
prices.compute("Shoes2", (key, value) -> 1810);
prices.compute("Shoes2", (key, value) -> 1820);
System.out.println(prices);
//给map中put值,有就 + 1,没有就添加初始化为0
Map<String, AtomicInteger> countTagClientMap = new LinkedHashMap<>();
countTagClientMap.computeIfAbsent("a", value -> new AtomicInteger()).incrementAndGet(); //这个可以实现为空就put,否则 + 1
countTagClientMap.computeIfAbsent("a", value -> new AtomicInteger());
System.out.println(countTagClientMap);
countTagClientMap.computeIfPresent("a", (key, value) -> new AtomicInteger(value.incrementAndGet()));
countTagClientMap.compute("a", (key, value) -> value == null ? new AtomicInteger(1) : new AtomicInteger(value.incrementAndGet())); //这个可以实现为空就put,否则 + 1
System.out.println(countTagClientMap);
}
4.2.TreeMap--排序操作
treeMap:treeMap默认是对key值升序排序
@Test
public void treeMap() {
// 对key值排序
TreeMap<Integer, Integer> map1 = new TreeMap<Integer, Integer>(); //默认的TreeMap升序排列
TreeMap<Integer, Integer> map2 = new TreeMap<Integer, Integer>(Comparator.reverseOrder());
map2.put(1, 2);
map2.put(2, 4);
map2.put(7, 1);
map2.put(5, 2);
System.out.println("Map2=" + map2);
map1.put(1, 2);
map1.put(2, 4);
map1.put(7, 1);
map1.put(5, 2);
System.out.println("map1=" + map1);
}
书山有路勤为径,学海无涯苦作舟。程序员不仅要懂代码,更要懂生活,关注我,一起进步。
Lambda--Optional、Collectors高级进阶方法的更多相关文章
- .Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?
本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码. 通过一系列优化最终达到两个效果,1.通过代码块来控制事务(分布式事务),2.通过委托优化Tran ...
- JavaSE | Lambda| Optional| Stream API
JDK1.8新特性 1.接口:默认方法. 静态方法 2.Lambda表达式和StreamAPI 3.Optional类 4.新的日期时间API Lambda表达式:为了简化代码,使得Java支持 St ...
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
- 高级进阶DB2(第2版)——内部结构、高级管理与问题诊断
<高级进阶DB2(第2版)——内部结构.高级管理与问题诊断> 基本信息 作者: 牛新庄 出版社:清华大学出版社 ISBN:9787302323839 上架时间:2013-7-3 出版 ...
- MEF高级进阶
MEF高级进阶 好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四 ...
- 《Android高级进阶》读书笔记
<Android高级进阶>是据我所知的市面上唯一一本技术工具书,比较的高大全,作者的目的是为了对全领域有个初步的概念 No1: 在Android系统中,拥有事件传递处理能力的类有以下三种 ...
- [总]Android高级进阶之路
个人Android高级进阶之路,目前按照这个目录执行,执行完毕再做扩展!!!!! 一.View的绘制 1)setContentView()的源码分析 2)SnackBar的源码分析 3)利用decor ...
- 高级进阶DB2(第2版)
<高级进阶DB2(第2版)> 基本信息 作者: 牛新庄 出版社:清华大学出版社 ISBN:9787302323839 上架时间:2013-7-3 出版日期:2013 年7月 开本:16开 ...
- Django笔记 —— 模型高级进阶
最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过.Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧- 本篇笔记(其 ...
随机推荐
- 《手把手教你》系列基础篇之(四)-java+ selenium自动化测试- 启动三大浏览器(下)基于Maven(详细教程)
1.简介 上一篇文章,宏哥已经在搭建的java项目环境中实践了,今天就在基于maven项目的环境中给小伙伴们 或者童鞋们演示一下. 2.eclipse中新建maven项目 1.依次点击eclipse的 ...
- js笔记8
1.js数据类型 基本数据类型:string.undefined.null.boolean.nember 引用数据类型:object.array.function 二者的区别? 基本的数据类型就是简单 ...
- 自定义组件开发:使用v-model封装el-pagination组件
1.前言 通过封装el-pagination组件开发自定义分页组件的类似文章网上已经有很多了,但看了一圈,总是不如意,于是决定还是自己动手搞一个. 2.背景 2.1.常规分页处理方法 利用el-pag ...
- 使用kubeadm进行k8s集群升级
一.目标 操作系统:CentOS Linux release 7.6.1810 (Core) 安装软件: docker:18.06.3-ce 从v1.15.5升级到v1.16.15 当前版本: [ro ...
- Linux搭建私有yum源
一.前期准备 环境:CentOS 8.3 镜像: CentOS-7-x86_64-Everything-2009.iso CentOS-8.3.2011-x86_64-dvd1.iso 二.搭建步骤 ...
- PostgreSQL角色问题
角色 PostgreSQL使用角色的概念管理数据库访问权限. 根据角色自身的设置不同,一个角色可以看做是一个数据库用户,或者一组数据库用户. 角色可以拥有数据库对象(比如表)以及可以把这些对象上的权限 ...
- Hadoop:什么是Hadoop??
官方讲解: Apache Hadoop 为可靠的,可扩展的分布式计算开发开源软件.Apache Hadoop软件库是一个框架,它允许使用简单的编程模型跨计算机群集分布式处理大型数据集(海量的数据). ...
- buu 简单注册器
一.本身对安卓逆向这块不是很熟悉,为了看这题稍微了解了一下,原来安卓虚拟机解释运行的是dex文件,和java的字节码不一样,然后是smail语法,这块我不会,所以找个了个神器来反编译2333,然后这题 ...
- linux驱动之获取设备树信息
上一篇文章学习了字符设备的注册,操作过的小伙伴都知道上一篇文章中测试驱动时是通过手动创建设备节点的,现在开始学习怎么自动挂载设备节点和设备树信息的获取,这篇文章中的源码将会是我以后编写字符驱动的模板. ...
- 续PA协商过程
续PA协商过程 当sw3的接口恢复之后会发生2中情况. ①sw3的G0/0/2口先发BPDU ②sw3的G0/0/3口先发BPDU sw3先发送BPDU sw3和sw1的交互过程: sw3的2口恢复后 ...