Java8特性Stream
参考博客: Java8新特性-尚硅谷笔记
一、Lambda
1.1 匿名函数
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
1.2 Java内置四大核心函数式接口Consumer
Consumer 消费型接口
Supplier 提供型接口
Function<T, R> 函数型接口
Predicate 断言型接口
1.3方法引用
对象 :: 实例方法
类 :: 静态方法
类 :: 实例方法
注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
二、Stream开始
Stream是数据渠道,用于操作数据源(集合和数组等),集合讲的是数据,流讲的是计算。
Stream自己不会存储元素
Stream不会改变源对象,会返回一个持有结果的新Stream
Stream操作是延迟的,这意味着会等到需要结果的时候才执行
Stream的三个操作:创建、中间操作、终止操作。
多个中间操作可以连接起来形成流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而在终止操作时一次性全部处理,称为惰性求值。
1. 流的常用创建方法
1.1 使用Collection下的 stream() 和 parallelStream() 方法
1.2创建流的几种方法
/**
* 创建流
*/
@Test
public void test01(){
/**
* 集合流
* - Collection.stream() 穿行流
* - Collection.parallelStream() 并行流
*/
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//数组流
//Arrays.stream(array)
String[] strings = new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
//无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++);
stream4.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
2.流的中间操作
2.1 筛选与切片
filter:过滤流中的某些元素
limit(n):获取n个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素
2.2 map映射
map:映射每个元素,如.mapToInt(),.mapToDouble(),如果元素为NULL会抛空指针异常。
可以在数据库设置字段非空,可以使用filter过滤元素
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
2.3 排序
<code> sorted():自然排序,流中元素需实现Comparable接口</code>
sorted(Comparator com):定制排序,自定义Comparator排序器
2.4 消费
peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
3.流的终止操作
3.1 匹配、聚合操作
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值
3.2 规约操作
归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
/**
* Java:
* - reduce:需提供默认值(初始值)
* Kotlin:
* - fold:不需要默认值(初始值)
* - reduce:需提供默认值(初始值)
*/
@Test
public void test01(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer integer = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(integer);
}
3.3 收集操作
collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。
3.4 Collector 工具库:Collectors
Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。
Collectors.toMap,把流所有的元素收集到一个Map,其键和值是将所提供的映射函数应用于输入元素的结果。
Collectors.toList(),把流所有的元素收集到一个List
Collectors.joining(", "),把流所有的元素以逗号连接到一个字符串
4.并行流 / 串行流
底层基于:Fork/Join实现框架
@Test
public void test03(){
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
}
5.Optional类
Optional.of(T t):创建一个 Optional 实例
Optional.empty(T t):创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
isPresent():判断是否包含某值
orElse(T t):如果调用对象包含值,返回该值,否则返回 t
orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
6.Java8接口中允许实现默认方法
接口中定义了一个默认方法,另外一个父类或者接口中又定义了一个同名方法。默认类优先
三、实践使用
参考【好】:Java 8 stream的详细用法
参考【好】:JDK1.8新特性Stream和Collectors19个常用示例总结
参考:Java8新特性之forEach+Lambda 表达式遍历Map和List
参考【好】:java 8 lambda表达式list操作分组、过滤、求和、最值、排序、去重
1.分组
2.过滤
3.求和
4.最值
5.List 转map
6.排序
7.去重
8.获取list某个字段组装新list
9.批量设置list列表字段为同一个值
10.不同实体的list拷贝
1.分组、求和(最大、最小、平均值)、map组装新list
//初始化
List<User> list = new ArrayList<User>();
list.add(new User("用户1", 1, 160.1, new BigDecimal("160.1")));
list.add(new User("用户1", 100, 160.2, new BigDecimal("160.1")));
list.add(new User("用户2", 10, 160.2, new BigDecimal("160.1")));
list.add(new User("用户2", 11, 160.2, new BigDecimal("160.1")));
list.add(new User("用户3", 18, 20.1, new BigDecimal("160.1")));
list.add(new User("用户3", 11, 10.1, new BigDecimal("160.1")));
list.add(new User("用户3", 12, 10.8, new BigDecimal("160.2")));
//分组求和Integer、Double、BigDecimal,返回Map格式
DecimalFormat df = new DecimalFormat("0.0");
Map<String,List<User>> groupMap = list.stream().collect(Collectors.groupingBy(User::getName));
Map<String,User> resultMap = new HashMap<>();
groupMap.forEach((k,v)->{
User dto = new User();
//Integer求和
dto.setAge(v.stream().mapToInt(User::getAge).sum());
// dto.setAge(v.stream().filter(x -> null!=x.getAge()).mapToInt(User::getAge).sum());
// dto.setAge(v.stream().mapToInt(x -> Convert.toInt(x.getAge(), 0)).sum());
//Double求和
dto.setHeight(Double.valueOf(df.format(v.stream().mapToDouble(User::getHeight).sum())));
//BigDecimal求和
dto.setDecimal(v.stream().map(User::getDecimal).reduce(BigDecimal.ZERO, BigDecimal::add));
resultMap.put(k, dto);
//测试最大、最小、平均值【以给默认值0的方式去空,有空时最小值会=0】
Integer maxAge = v.stream().mapToInt(x -> Convert.toInt(x.getAge(),0)).max().getAsInt();
Integer minAge = v.stream().mapToInt(x -> Convert.toInt(x.getAge(),0)).min().getAsInt();
//2种方式平均值,目标是Integer时,sum()/v.size()会转int丢失小数部分
Double avgAge = Double.valueOf(df.format((v.stream().mapToInt(x -> Convert.toInt(x.getAge(),0)).average().getAsDouble())));
Double avgAge2 = Double.valueOf(df.format((v.stream().mapToInt(x -> Convert.toInt(x.getAge(),0)).sum()/v.size())));
Double avgHeight = Double.valueOf(df.format((v.stream().mapToDouble(User::getHeight).average().getAsDouble())));
Double avgHeight2 = Double.valueOf(df.format((v.stream().mapToDouble(User::getHeight).sum()/v.size())));
});
//获取list某个字段组装新list
List<String> names = list.stream().map(x -> x.getName()).collect(Collectors.toList());
List<Integer> ages = list.stream().map(x -> x.getAge()).collect(Collectors.toList());
2.排序,自定义过滤
参考:java8新特性stream,按照某个字段排序,以及取出前几条数据
//.reversed()倒序排序,去掉.reversed()正序排列
list = list.stream().sorted(Comparator.comparing(ExamGrades1::getYear).reversed()).collect(Collectors.toList());
//按照num倒叙排列并且取出前两条数据
List<ViewStoreStockLib> collect = libs2.stream().sorted(Comparator.comparing(ViewStoreStockLib::getNum).reversed()).limit(2).collect(Collectors.toList());
//自定义过滤:不等于name
public static boolean filterName(String name, User user) {
if(!StrUtil.equals(name, user.getName())) {
return true;
}
return false;
}
//过滤:name不等于用户1
List<User> resultList = list.stream().filter(x -> !StrUtil.equals("用户1", x.getName())).collect(Collectors.toList());
List<User> resultList2 = list.stream().filter(x -> TestSen.filterName("用户1", x)).collect(Collectors.toList());
3.合并list,去重,分页,排序
参考:Java8 stream 根据对象字段去重
参考:No value present 异常处理
List<List<UserInfo>> collect = new ArrayList<>();
省略...
collect.add(list1);
collect.add(list2
//合并
List<UserInfo> noUniqueList = collect.stream().flatMap(l -> l.stream()).collect(Collectors.toList());
//如果主持人不存在
boolean flag = noUniqueList.stream().filter(x -> StrUtil.equals(lecture.getTrainUser(), x.getId())).findAny().isPresent();
if(!flag) {
UserInfo o = lectureMapper.getUserByid(lecture.getTrainUser());
noUniqueList.add(o);
}
//使用TreeSet去重
List<UserInfo> unique1 = noUniqueList.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getId()))),
ArrayList::new));
//如果集合中不存在元素,.get()会报错No value present
Optional<SelfGrades> optional = gradesList1.stream().filter(x -> StrUtil.equals(x.getPersonId(), planUser.getUserId())).findAny();
SelfGrades o = optional.isPresent() ? optional.get() : null;
//分页
public static <T> List<T> paging(List<T> list, int page, int limit) {
List<T> result = list.stream()
.skip((page - 1) * limit)
.limit(limit)
.collect(Collectors.toList());
return result;
}
//如果集合中有一个元素满足条件就返回true
boolean flag = list.stream().anyMatch(x -> x.getQuarterlyCode()==quaCode && StrUtil.equals(x.getOrganCode(), orgCode));
//将多个集合中的元素合并成一个集合
List<NetProgressVo> pageList = Stream.of(list, list2).flatMap(x -> x.stream()).collect(Collectors.toList());
//过滤搜索,是否网报
int noFilterCount = pageList.size();//是否网报过滤前总count
if(!StrUtil.isBlank(rest.getReportStatus())) {
pageList = pageList.stream().filter(x -> StrUtil.equals(x.getReportStatus(), rest.getReportStatus())).collect(Collectors.toList());
}
//.reversed()倒序排序,去掉.reversed()正序排列
pageList = pageList.stream().sorted(Comparator.comparing(NetProgressVo::getQuarterlyCode).reversed()).collect(Collectors.toList());
int allCount = pageList.size();//分页前总count
//分页查询
List<NetProgressVo> resList = MonitorService.paging(pageList, rest.getPage(), rest.getLimit());