Java Stream流操作
- 1. 获取stream流
- 2. 串行流、并行流
- 3. 遍历(forEach / forEachOrdered)
- 4. 查找(findFirst / findAny)
- 5. 匹配 (anyMatch / allMatch/ noneMatch)
- 6. 过滤(filter)
- 7. map / flatMap / peek
- 8. 集合组装(toList / toSet/ toMap)
- 9. 排序(sort)
- 10. 拼接(joining)
- 11. 合并(collect) + 去重(distinct) + 限制(limit) + 跳过(skip)
- 12. 分区(partitioningBy) | 分组(groupingBy)
- 13. 统计运算
1. 获取stream流
1.1 集合Collection类型 (包含List、Set等)
java.util.Collection.stream()
List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);}};
list.stream(); // 生成Stream对象(串行流)
1.2 数组类型
int[] array={1,2};
Arrays.stream(array); // 生成Stream对象(串行流)
2. 串行流、并行流
- 串行流和并行流粗暴的理解就是处理的时候是单线程处理,还是多线程处理
- 并行流的处理效率高于串行流,适用于对数据处理没有顺序要求的场景
list.stream(); // 串行流 (主线程按顺序对流执行操作)
list.parallelStream(); // 并行流 (以多线程并行执行的方式对流进行操作)
list.stream().parallel(); // 将串行流转换为并行流
3. 遍历(forEach / forEachOrdered)
- forEach: 串行流是有序遍历,并行流时无序遍历。
- forEachOrdered:有序遍历。即使并行流也是有序遍历!!!
List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);add(3);add(4);add(5);add(6);}};
list.stream().forEach(i -> {
System.out.println(i);
});
list.stream().forEachOrdered(i -> {
System.out.println(i);
});
4. 查找(findFirst / findAny)
List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);add(3);add(4);add(5);add(6);}};
// Optional如果值存在则isPresent()方法会返回true,调用get()方法会返回存储的对象。
Optional<Integer> optional = list.stream().findFirst(); // 返回流中的第一个对象
if(optional.isPresent()){
System.out.println(optional.get());
}
Optional<Integer> optional1 = list.stream().findAny(); // 返回流中的任一对象
if(optional1.isPresent()){
System.out.println(optional1.get());
}
5. 匹配 (anyMatch / allMatch/ noneMatch)
List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);add(3);add(4);add(5);add(6);}};
Boolean flag = list.stream().anyMatch(i -> i > 3); // true 是否存在大于3的值
Boolean flag1 = list.stream().allMatch(i -> i > 3); // false 是否每个值都大于3
Boolean flag2 = list.stream().noneMatch(i -> i > 3); // false 是否不存在大于3的值
6. 过滤(filter)
List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);add(3);add(4);add(5);add(6);}};
Stream<Integer> stream = list.stream().filter(i -> i >3); // 过滤出大于3的值
stream.forEach(System.out::print); // 打印结果:456
7. map / flatMap / peek
- 将原始流中的元素按照传入的函数一个一个执行一遍,把每个函数return的结果组装成一个新的流。
- 注意:元素经过处理后,原始的流的数据等于被修改!!!如果想不变就new一个新对象作为来操作~
7.1 区别
- map
- 原始流中每个元素执行一遍函数,函数return的结果可以为任意类型,该结果作为一个新的元素
- 这些新的元素组成一个新的流
- 最终产生的流中元素个数和原始流中元素个数一致
- 也就是说将原始流中元素一个一个变成新的样貌,只是变了样子,然后再把他们收集起来
- flatMap:
- 原始流中每个元素执行一遍函数,函数return的结果必须是一个流类型。(元素变成了流。而不是元素改个样子,还作为元素!!!)
- 将这些流连接在一起
- 最终产生的流中元素个数和原始流中元素个数是可能不一致的
- 原始流中的每个元素都变成了流,那么就是元素升级了
- 因为每个流中本身就可以有多个元素,所以最终的流中元素数量会出现比原来的流的元素数量还多的可能
7.2 演示
@Slf4j
public class StreamMainTest {
public static void main(String[] args) throws JsonProcessingException {
// 部门发奖金10W,奖金表已经安排好了
List<Employee> employees = new ArrayList<Employee>(){{
add(new Employee("Tom", 10000));
add(new Employee("Lily", 15000));
add(new Employee("Jack", 20000));
add(new Employee("Kate", 25000));
add(new Employee("Nancy", 30000));
}};
// 想到还有几个新来的,领导说新来的也给点福利
// 方案1:大家先一人扣2000,然后再商量怎么给小弟
List<Employee> employees1 = employees.stream().map(employee -> {
return new Employee(employee.getName(), employee.getBonus() - 2000);
}).collect(Collectors.toList());
log.info("方案1:{}", new ObjectMapper().writeValueAsString(employees1));
// 方案2:奖金大于2W的都是师傅,把多的部分给自己带的新人
List<Employee> employees2 = employees.stream().flatMap(employee -> {
List<Employee> result = new ArrayList<>();
if (employee.getBonus() > 20000) {
result.add(new Employee(employee.getName(), 20000)); // 大于2W的,那你拿2W吧
result.add(new Employee(employee.getName() + "-小弟", employee.getBonus() - 20000));// 多的给小弟
} else {
result.add(employee);
}
return result.stream(); // 返回流
}).collect(Collectors.toList());
log.info("方案2:{}", new ObjectMapper().writeValueAsString(employees2));
}
@Data
@AllArgsConstructor
static class Employee{
private String name;
private int bonus;
}
}
输出结果
方案1:[
{"name":"Tom","bonus":8000},
{"name":"Lily","bonus":13000},
{"name":"Jack","bonus":18000},
{"name":"Kate","bonus":23000},
{"name":"Nancy","bonus":28000}]
方案2:[
{"name":"Tom","bonus":10000},
{"name":"Lily","bonus":15000},
{"name":"Jack","bonus":20000},
{"name":"Kate","bonus":20000},
{"name":"Kate-小弟","bonus":5000},
{"name":"Nancy","bonus":20000},
{"name":"Nancy-小弟","bonus":10000}]
7.3 peek
- 跟map相似的还有个peek
- 差别就是map可以返回任意类型
- peek是在原数据上做修改,可以理解为返回的还是当前数据类型,只是数据被修改了,类型不变
- 一般用map就好了,可以用peek的时候,IDEA会提示你的,直接帮你把map优化为peek
8. 集合组装(toList / toSet/ toMap)
employees.stream().collect(Collectors.toList());
employees.stream().collect(Collectors.toSet());
employees.stream().collect(Collectors.toMap(employee -> employee.getName(), employee -> employee));
9. 排序(sort)
List<Employee> employees = new ArrayList<Employee>() {{
add(new Employee("Jack", 20000));
add(new Employee("Tom", 10000));
add(new Employee("Nancy", 30000));
add(new Employee("Lily", 15000));
add(new Employee("Kate", 25000));
}};
// 正序
List<Employee> newEmployees = employees.stream().sorted(Comparator.comparing(Employee::getBonus)).collect(Collectors.toList());
// 倒序
employees.stream().sorted(Comparator.comparing(Employee::getBonus).reversed()).collect(Collectors.toList());
10. 拼接(joining)
- 使用指定字符将特定内容拼接成一个字符串
String nameStr = employees.stream().map(Employee::getName).collect(Collectors.joining("-"));
11. 合并(collect) + 去重(distinct) + 限制(limit) + 跳过(skip)
List<Employee> employees1 = new ArrayList<Employee>() {{
add(new Employee("Tom", 10000));
add(new Employee("Lily", 15000));
}};
List<Employee> employees2 = new ArrayList<Employee>() {{
add(new Employee("Tom", 10000));
add(new Employee("Lily", 15000));
add(new Employee("Jack", 20000));
add(new Employee("Kate", 25000));
add(new Employee("Nancy", 30000));
}};
// 合并
List<Employee> employees3 = Stream.concat(employees1.stream(), employees2.stream()).collect(Collectors.toList());
// 去重 (TODO 去重原理,如何判定重复???)
List<Employee> employees4 = employees3.stream().distinct().collect(Collectors.toList());
// 取前几个元素
List<Employee> employees5 = employees4.stream().limit(3).collect(Collectors.toList());
// 跳过前几个,取后续元素
List<Employee> employees6 = employees4.stream().skip(3).collect(Collectors.toList());
// skip 和 limit 结合,取中间元素
List<Employee> employees7 = employees4.stream().skip(2).limit(2).collect(Collectors.toList());
12. 分区(partitioningBy) | 分组(groupingBy)
- 分区:使用断言方式分区(只有两个区:true, false)
- 分组:使用函数方式分组(可以有多个组)
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
List<Employee> employees = new ArrayList<Employee>() {{
add(new Employee("Tom", 1, 10000));
add(new Employee("Lily", 0, 15000));
add(new Employee("Jack", 1, 20000));
add(new Employee("Kate", 0, 25000));
add(new Employee("Nancy", 0, 30000));
}};
// 按照奖金是否大于2W分区:
Map<Boolean, List<Employee>> part = employees.stream().collect(Collectors.partitioningBy(employee -> employee.getBonus() > 20000));
log.info("按照奖金是否大于2W分区 {}", objectMapper.writeValueAsString(part));
// 按照性别分组
Map<Integer, List<Employee>> group = employees.stream().collect(Collectors.groupingBy(Employee::getSex));
log.info("按照性别分组 {}", objectMapper.writeValueAsString(group));
// 先分区再分组
Map<Boolean, Map<Integer, List<Employee>>> part_group = employees.stream().collect(Collectors.partitioningBy(employee -> employee.getBonus() > 20000, Collectors.groupingBy(Employee::getSex)));
log.info("先分区再分组 {}", objectMapper.writeValueAsString(part_group));
// 先分组再分区
Map<Integer, Map<Boolean, List<Employee>>> group_part = employees.stream().collect(Collectors.groupingBy(Employee::getSex, Collectors.partitioningBy(employee -> employee.getBonus() > 20000)));
log.info("先分组再分区 {}", objectMapper.writeValueAsString(group_part));
}
@Data
@AllArgsConstructor
public static class Employee {
private String name;
private int sex;
private int bonus;
}
输出
按照奖金是否大于2W分区
{
"false": [{
"name": "Tom",
"sex": 1,
"bonus": 10000
}, {
"name": "Lily",
"sex": 0,
"bonus": 15000
}, {
"name": "Jack",
"sex": 1,
"bonus": 20000
}],
"true": [{
"name": "Kate",
"sex": 0,
"bonus": 25000
}, {
"name": "Nancy",
"sex": 0,
"bonus": 30000
}]
}
按照性别分组
{
"0": [{
"name": "Lily",
"sex": 0,
"bonus": 15000
}, {
"name": "Kate",
"sex": 0,
"bonus": 25000
}, {
"name": "Nancy",
"sex": 0,
"bonus": 30000
}],
"1": [{
"name": "Tom",
"sex": 1,
"bonus": 10000
}, {
"name": "Jack",
"sex": 1,
"bonus": 20000
}]
}
先分区再分组
{
"false": {
"0": [{
"name": "Lily",
"sex": 0,
"bonus": 15000
}],
"1": [{
"name": "Tom",
"sex": 1,
"bonus": 10000
}, {
"name": "Jack",
"sex": 1,
"bonus": 20000
}]
},
"true": {
"0": [{
"name": "Kate",
"sex": 0,
"bonus": 25000
}, {
"name": "Nancy",
"sex": 0,
"bonus": 30000
}]
}
}
先分组再分区
{
"0": {
"false": [{
"name": "Lily",
"sex": 0,
"bonus": 15000
}],
"true": [{
"name": "Kate",
"sex": 0,
"bonus": 25000
}, {
"name": "Nancy",
"sex": 0,
"bonus": 30000
}]
},
"1": {
"false": [{
"name": "Tom",
"sex": 1,
"bonus": 10000
}, {
"name": "Jack",
"sex": 1,
"bonus": 20000
}],
"true": []
}
}
13. 统计运算
13.1 统计A:
- max:最大
- min:最小
- count:数量
// 奖金最高的出来
Optional<Employee> optional = employees.stream().max(Comparator.comparing(Employee::getBonus));
if(optional.isPresent()){
System.out.println(optional.get().getName());
}
// 奖金最低的出来
Optional<Employee> optional1 = employees.stream().min(Comparator.comparing(Employee::getBonus));
if(optional1.isPresent()){
System.out.println(optional1.get().getName());
}
// 女的有几个人啊
long count = employees.stream().filter(employee -> employee.getSex() == 0).count();
System.out.println(count);
13.2 统计B:
- maxBy:最大
- minBy:最小
- averagingInt:平均
- summingInt:总和
- counting:数量
- summarizingInt:以上全部
List<Employee> employees = new ArrayList<Employee>() {{
add(new Employee("Tom", 1, 10000));
add(new Employee("Lily", 0, 15000));
add(new Employee("Jack", 1, 20000));
add(new Employee("Kate", 0, 25000));
add(new Employee("Nancy", 0, 30000));
}};
Optional<Integer> max = employees.stream().map(Employee::getBonus).collect(Collectors.maxBy(Integer::compareTo));
System.out.println("最大奖金:" + max);
Optional<Integer> min = employees.stream().map(Employee::getBonus).collect(Collectors.minBy(Integer::compareTo));
System.out.println("最低奖金:" + min);
Double averag = employees.stream().collect(Collectors.averagingInt(Employee::getBonus));
System.out.println("平均奖金:" + averag);
Integer sum = employees.stream().collect(Collectors.summingInt(Employee::getBonus));
System.out.println("奖金总额:" + sum);
Long count = employees.stream().collect(Collectors.counting());
System.out.println("奖金份数:" + count);
IntSummaryStatistics summarizing = employees.stream().collect(Collectors.summarizingInt(Employee::getBonus));
System.out.println("最大奖金:" + summarizing.getMax());
System.out.println("最低奖金:" + summarizing.getMin());
System.out.println("平均奖金:" + summarizing.getAverage());
System.out.println("奖金总额:" + summarizing.getSum());
System.out.println("奖金份数:" + summarizing.getCount());
13.3 运算 reduce(归约)
List<Double> list = new ArrayList<Double>(){{add(1d);add(2d);add(3d);add(4d);add(5d);add(6d);}};
Optional<Double> max = list.stream().reduce((x, y) -> x > y ? x : y);
System.out.println("大: " + max);
Optional<Double> min = list.stream().reduce((x, y) -> x < y ? x : y);
System.out.println("小: " + min);
Optional<Double> sum = list.stream().reduce((x, y) -> x + y);
System.out.println("加: " + sum);
Double subtract = list.stream().reduce(10000d, (x, y) -> x - y);
System.out.println("减: " + subtract);
Optional<Double> product = list.stream().reduce((x, y) -> x * y);
System.out.println("乘: " + product);
Double divide = list.stream().reduce(10000d, (x, y) -> x / y);
System.out.println("除: " + divide);
输出
大: Optional[6.0]
小: Optional[1.0]
加: Optional[21.0]
减: 9979.0
乘: Optional[720.0]
除: 13.888888888888891