jdk8中出现了stream,lambda表达式等,对操作集合提供了很快捷的操作;
主要功能实现:将操作元素当成流来出来,然后调用stream API方法,对元素进行操作。
流的操作类型分为两种:
Intermediate(中间的):一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal(终端):一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
在对一个Stream进行多次转换操作(Intermediate 操作),每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是N(转换次数)个for循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是lazy的,多个转换操作只会在Terminal操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal 操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。
还有一种操作被称为short-circuiting。用以指:对于一个intermediate操作,如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新Stream;对于一个terminal操作,如果它接受的是一个无限大的Stream,但能在有限的时间计算出结果。
当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个short-circuiting操作是必要非充分条件。
可以创建并行流和串行流
两者之间的区别详见:https://blog.csdn.net/weixin_43987718/article/details/124388021
并行流也可以通过:
集合创建流
List<String> strList = Arrays.asList("a", "b", "c", "d");
// 串行流
List<String> list = strList.stream().filter(x -> x.equals("a")).collect(Collectors.toList());
// 并行流创建的两种方式
List<String> list2 = strList.parallelStream().filter(x -x.equals("a")).collect(Collectors.toList());
List<String> list2 = strList.stream().parallel().filter(x -> x.equals("a")).collect(Collectors.toList());
也可以通过Arrays来创建
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();1234567891011
创建一个实体
@Data
public class Person {
private String name;
private String sex;
private int age;
private int salary;
private String country;
Person(String name ,String sex,int age,int salary,String country){
this.salary = salary;
this.name = name;
this.sex = sex;
this.age = age;
this.country = country;
}
}
操作:
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2022/05/06 15:58:56
*/
public class StreamDemo2 {
public static void main(String[] args) {
// 流的常用操作,筛选,匹配,遍历
List<Person> personList = new ArrayList<>();
personList.add(new Person("小张","男",18,3200,"南京"));
personList.add(new Person("小李","女",18,8200,"连云港"));
personList.add(new Person("小王","女",22,10200,"徐州"));
personList.add(new Person("小刘","男",24,8200,"苏州"));
personList.add(new Person("小梁","女",26,5200,"上海"));
personList.add(new Person("小六","男",19,9200,"无锡"));
// 遍历
personList.stream().forEach(p->{
System.out.println(p);
});
// 筛选
// 筛选大于20岁的第一个
Optional<Person> first = personList.stream().filter(p -> p.getAge() > 20).findFirst();
// findAny 常和并行流一起搭配使用
Optional<Person> firs2 = personList.stream().parallel().filter(p -> p.getAge() > 20).findAny();
// 是否存在大于20岁的
boolean b = personList.stream().anyMatch(p -> p.getAge() > 20);
System.out.println("筛选大于20岁的第一个"+ JSONObject.toJSONString(first));
System.out.println("筛选大于20岁的任意一个"+ JSONObject.toJSONString(firs2));
System.out.println("是否存在大于20岁的"+ b);
}
}
结果:
Person(name=小张, sex=男, age=18, salary=3200, country=南京)
Person(name=小李, sex=女, age=18, salary=8200, country=连云港)
Person(name=小王, sex=女, age=22, salary=10200, country=徐州)
Person(name=小刘, sex=男, age=24, salary=8200, country=苏州)
Person(name=小梁, sex=女, age=26, salary=5200, country=上海)
Person(name=小六, sex=男, age=19, salary=9200, country=无锡)
筛选大于20岁的第一个{"age":22,"country":"徐州","name":"小王","salary":10200,"sex":"女"}
筛选大于20岁的任意一个{"age":24,"country":"苏州","name":"小刘","salary":8200,"sex":"男"}
是否存在大于20岁的true
筛选出来工资大于8000的小伙伴,并生成新的集合
// 筛选出来工资大于8000的小伙伴,并生成新的集合
List<Person> collect = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toList());
// 筛选出来工资大于8000的小伙伴,并生成新名称的集合
List<String> collect1 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
System.out.println("筛选出来工资大于8000的小伙伴"+ JSONObject.toJSONString(collect));
System.out.println("筛选出来工资大于8000的小伙伴的名称"+ JSONObject.toJSONString(collect1));
结果:
筛选出来工资大于8000的小伙伴[{"age":18,"country":"连云港","name":"小李","salary":8200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":10200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":8200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":9200,"sex":"男"}]
筛选出来工资大于8000的小伙伴的名称["小李","小王","小刘","小六"]
1)获取居住地名称最长的
// 获取家乡名称最长的
Optional<String> max = personList.stream().map(Person::getCountry).max(Comparator.comparing(String::length));
System.out.println("人员居住地字符串长度最长的:"+max);
输出:
人员居住地字符串长度最长的:Optional[连云港]
2)筛选出来年龄最小的
// 筛选出来年龄最小
Optional<Integer> min = personList.stream().map(Person::getAge).min(Integer::compareTo);
System.out.println("年龄最小的人员:"+min);
// 也可以自定义比较规则,Comparator
Optional<Integer> min1 = personList.stream().map(Person::getAge).min(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("年龄最小的人员:"+min1);
输出:
年龄最小的人员:Optional[18]
年龄最小的人员:Optional[18]
3)获取工资大于8k的人员个数
// 获取工资大于8k的个数
long count = personList.stream().map(Person::getSalary).filter(p -> p > 8000).count();
System.out.println("获取工资大于8k的个数"+count);
输出:
获取工资大于8k的个数4
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map
和flatMap
:
map
:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。flatMap
:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。// 所有员工工资加上1k 1、不改变原来的集合,2、改变原来集合
// 1、不改变原来的集合,
List<Person> collect2 = personList.stream().map(person -> {
Person person1 = new Person(person.getName(), person.getSex(), person.getAge(), person.getSalary() + 1000, person.getCountry());
return person1;
}).collect(Collectors.toList());
System.out.println("1、不改变原来的集合,"+JSONObject.toJSONString(collect2));
// 改变原来集合
List<Person> collect3 = personList.stream().map(person -> {
person.setSalary(person.getSalary() + 1000);
return person;
}).collect(Collectors.toList());
System.out.println("2、改变原来集合"+collect3);
输出:
1、不改变原来的集合,[{"age":18,"country":"南京","name":"小张","salary":4200,"sex":"男"},{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":26,"country":"上海","name":"小梁","salary":6200,"sex":"女"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]
2、改变原来集合[Person(name=小张, sex=男, age=18, salary=4200, country=南京), Person(name=小李, sex=女, age=18, salary=9200, country=连云港), Person(name=小王, sex=女, age=22, salary=11200, country=徐州), Person(name=小刘, sex=男, age=24, salary=9200, country=苏州), Person(name=小梁, sex=女, age=26, salary=6200, country=上海), Person(name=小六, sex=男, age=19, salary=10200, country=无锡)]
Disconnected from the target VM, address: '127.0.0.1:8836', transport: 'socket'
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
// reduce 归约
// 求和
Optional<Integer> reduce = personList.stream().map(Person::getSalary).reduce(Integer::sum);
System.out.println("求和:"+reduce.get());
// 求和方式2
int sum = personList.stream().mapToInt(Person::getSalary).sum();
System.out.println("求和2:"+sum);
// 求和方式3
Optional<Integer> reduce3 = personList.stream().map(Person::getSalary).reduce((x, y) -> x + y);
System.out.println("求和3:"+reduce3.get());
// 求和方式4
Integer reduce1 = personList.stream().map(Person::getSalary).reduce(1, Integer::sum);
System.out.println("求和4:"+reduce3.get());
// 求最大值
Optional<Integer> reduce2 = personList.stream().map(Person::getSalary).reduce((x, y) -> x > y ? x : y);
System.out.println("求最大值:"+reduce2.get());
// 求最大值
Integer reduce4 = personList.stream().map(Person::getSalary).reduce(1, Integer::max);
System.out.println("求最大值:"+reduce4);
// 求乘积
Optional<Integer> reduce5 = personList.stream().map(Person::getSalary).reduce((x, y) -> x * y);
System.out.println("求乘积:"+reduce5.get());
int i = 1;
for (Person person : personList){
i = i * person.getSalary();
}
System.out.println("求乘积:"+i);
输出结果:
求和:50200
求和2:50200
求和3:50200
求和4:50200
求最大值:11200
求最大值:11200
求乘积:-1149239296
求乘积:-1149239296
流最终的操作还是要把处理过的数据给收集起来,可能是原来的集合,也可能是一个新的集合
// collect收集
// 常用的toList,toMap,toSet等
// 工资大于8k的集合
List<Person> collect4 = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toList());
System.out.println("工资大于8k的集合:"+JSONObject.toJSONString(collect4));
// 工资大于8k的人员姓名
List<String> collect5 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
System.out.println("工资大于8k的人员姓名:"+JSONObject.toJSONString(collect5));
// 工资大于8k的人员姓名 set
Set<String> collect6 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toSet());
System.out.println("工资大于8k的人员姓名:"+JSONObject.toJSONString(collect6));
// 工资大于8k的人员姓名 map
Map<String, Integer> collect7 = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName, Person::getSalary));
System.out.println("工资大于8k的人员姓名map:"+JSONObject.toJSONString(collect7));
根据指定字段去重
保留第一条记录
ArrayList<Person> collect = list.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(
Comparator.comparing(tc -> tc.getSalary())
)
), ArrayList::new
)
);
保留最后一条记录,使用map
List<Person> collect = list.stream().collect(
Collectors.toMap(Person::getSalary, Function.identity(), (a1, a2) -> a2, LinkedHashMap::new)
).values().stream().collect(Collectors.toList());
结果:
工资大于8k的集合:[{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]
工资大于8k的人员姓名:["小李","小王","小刘","小六"]
工资大于8k的人员姓名:["小刘","小李","小六","小王"]
工资大于8k的人员姓名map:{"小刘":9200,"小李":9200,"小六":10200,"小王":11200}
// 常用的一些静态统计方法 count/average
// Collectors 用于统计的一系列静态方法
// 计数:count
// 平均值:averagingInt、averagingLong、averagingDouble
// 最值:maxBy、minBy
// 求和:summingInt、summingLong、summingDouble
// 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
Long collect8 = personList.stream().collect(Collectors.counting());
System.out.println("人员数量:"+collect8);
// 统计工资平均值
Double collect9 = personList.stream().collect(Collectors.averagingInt(Person::getSalary));
System.out.println("工资平均值:"+collect9);
// 统计工资最大值
Optional collect10 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compareTo));
System.out.println("工资最大值:"+collect10.get());
// 工资求和
Integer collect11 = personList.stream().collect(Collectors.summingInt(Person::getSalary));
System.out.println("工资求和:"+collect11);
// 统计以上所有
IntSummaryStatistics collect12 = personList.stream().collect(Collectors.summarizingInt(Person::getSalary));
System.out.println("统计所有:"+collect12);
结果:
人员数量:6
工资平均值:8366.666666666666
工资最大值:11200
工资求和:50200
统计所有:IntSummaryStatistics{count=6, sum=50200, min=4200, average=8366.666667, max=11200}
partitioningBy:只能分为两组,且key是boolean型
groupingBy:没有限制,key是string
// 分组(partitioningBy/groupingBy)
Map<Boolean, List<Person>> collect13 = personList.stream().collect(Collectors.partitioningBy(p -> p.getSalary() > 8000));
System.out.println("boolean分组数据:"+JSONObject.toJSONString(collect13));
// 按照性别分组
Map<String, List<Person>> collect14 = personList.stream().collect(Collectors.groupingBy(Person::getSex));
System.out.println("性别分组:"+JSONObject.toJSONString(collect14));
// 按照性别分组,在按照地区分组
Map<String, Map<String, List<Person>>> collect15 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getCountry)));
System.out.println("性别分组、地区分组:"+JSONObject.toJSONString(collect15));
结果:
boolean分组数据:{
false:[{"age":18,"country":"南京","name":"小张","salary":4200,"sex":"男"},{"age":26,"country":"上海","name":"小梁","salary":6200,"sex":"女"}],
true:[{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]}
性别分组:{"女":[{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":26,"country":"上海","name":"小梁","salary":6200,"sex":"女"}],"男":[{"age":18,"country":"南京","name":"小张","salary":4200,"sex":"男"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]}
性别分组、地区分组:{"女":{"上海":[{"age":26,"country":"上海","name":"小梁","salary":6200,"sex":"女"}],"徐州":[{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"}],"连云港":[{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"}]},"男":{"无锡":[{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}],"苏州":[{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"}],"南京":[{"age":18,"country":"南京","name":"小张","salary":4200,"sex":"男"}]}}
Disconnected from the target VM, address: '127.0.0.1:3685', transport: 'socket'
// join 连接
String collect16 = personList.stream().map(Person::getName).collect(Collectors.joining(","));
System.out.println("join连接:"+collect16);
结果:
join连接:小张,小李,小王,小刘,小梁,小六
package com.example.Stream;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2022/05/13 13:25:05
*/
public class StreamDemo3 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("小张","男",18,3200,"南京"));
personList.add(new Person("小李","女",18,8200,"连云港"));
personList.add(new Person("小王","女",22,10200,"徐州"));
personList.add(new Person("小刘","男",24,8200,"苏州"));
personList.add(new Person("小梁","女",26,5200,"上海"));
personList.add(new Person("小六","男",19,9200,"无锡"));
// 排序 sorted 调用Compator方法,或者自定义方法
// 自认排序升序
List<String> collect = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
System.out.println("升序:"+ JSONObject.toJSONString(collect));
// 降序排序 reversed 降序
List<String> collect1 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
System.out.println("降序:"+JSONObject.toJSONString(collect1));
// 按照工资排序,工资一样的话,按照年龄排序
List<String> collect2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
System.out.println(" 按照工资排序,工资一样的话,按照年龄排序:"+collect2);
// 自定义排序
List<String> collect3 = personList.stream().sorted((p1,p2)->{
if (p1.getSalary() == p2.getSalary()){
return p1.getAge() - p2.getAge();
}else {
return p1.getSalary() - p2.getSalary();
}
}).map(Person::getName).collect(Collectors.toList());
System.out.println("自定义排序:"+collect3);
}
}
自定义的排序的return 语句 ==> 三元运算符
结果:
升序:["小张","小梁","小李","小刘","小六","小王"]
降序:["小王","小六","小李","小刘","小梁","小张"]
按照工资排序,工资一样的话,按照年龄排序:[小张, 小梁, 小李, 小刘, 小六, 小王]
自定义排序:[小张, 小梁, 小李, 小刘, 小六, 小王]
package com.example.Stream;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 描述
*
* @author LZH
* @version 1.0
* @date 2022/05/13 13:25:05
*/
public class StreamDemo4 {
public static void main(String[] args) {
String [] arr1 = {"a","b","c","d"};
String [] arr2 = {"a","b","c","e"};
// 流的去重distinct,跳过skip,限制
// 流的合并
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
List<String> collect = Stream.concat(stream2, stream1).collect(Collectors.toList());
System.out.println("流的合并:"+JSONObject.toJSONString(collect));
// 去重
List<String> collect1 = collect.stream().distinct().collect(Collectors.toList());
System.out.println("去重:"+JSONObject.toJSONString(collect1));
// 限制 整数,从1开始,每隔4个取一值,取前10个
List<Integer> collect2 = Stream.iterate(1, x -> x + 4).limit(10).collect(Collectors.toList());
System.out.println("限制:"+JSONObject.toJSONString(collect2));
// 跳过 跳过的位数
List<Integer> collect3 = Stream.iterate(1, x -> x + 4).skip(5).limit(5).collect(Collectors.toList());
System.out.println("跳过:"+JSONObject.toJSONString(collect3));
}
}
结果:
流的合并:["a","b","c","e","a","b","c","d"]
去重:["a","b","c","e","d"]
限制:[1,5,9,13,17,21,25,29,33,37]
跳过:[21,25,29,33,37]