文章目录
1. 概述
1.1 流的概述
流在管道中传输,且可以在管道的节点上进行处理,比如排序,聚合,过滤等操作
1.2 操作详情
- 元素书记便是原始集合,如List、Set、Map等
- 生成流,可以是串行流stream()或者并行流parallelStream()
- 中间操作,可以是排序、聚合、过滤、转换等
- 终端操作,很多流操作本会就会返回一个流,所以多个操作可以直接连接起来,最后统一进行收集
- 概念stream接口源码
1.3 简单实例
List<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
list.add("test3");
list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());
2. map、filter和flatMap函数
2.1 map函数
- 将流中的每个元素T映射为R(类似于类型转换)
- 场景转换,如javaweb开发中集合里面的DO对象转换为DTO对象
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
List<Integer> integerList = list.stream().map(Integer::parseInt).collect(Collectors.toList());
System.out.println(integerList);
2.2 filter函数
- 用于通过设置的条件过滤出元素
// 过滤出字符长度大于5的
List<String> list = new ArrayList<>();
list.add("test1");
list.add("test12");
list.add("test123");
List<String> resultList = list.stream().filter(str -> str != null && str.length() > 5).collect(Collectors.toList());
System.out.println(resultList);
2.3 flatMap函数
flatMap方法接受一个lambda表达式函数,函数的返回值必须也是一个stream类型,flatMap方法最终会把所有返回的stream合并
List<String> list1 = new ArrayList<>(2);
list1.add("test1");
list1.add("test2");
List<String> list2 = new ArrayList<>(2);
list2.add("test4");
list2.add("test5");
List<List<String>> lists = new ArrayList<>(2);
lists.add(list1);
lists.add(list2);
lists.stream()
// 将其中所有的list合并成一个流输出
.flatMap(list -> list.stream())
.forEach(str -> System.out.println(str));
2.4 注意事项
map与filter函数都是惰性求值操作,在调用collect、count等及早求值操作前,不会执行函数中的内部代码
List<String> list = new ArrayList<>(3);
list.add("test1");
list.add("test2");
list.add("tes");
// 并不会打印11
list.stream().filter(str -> {
System.out.println("11");
return str.startsWith("test");
});
3.sorted与limit函数
3.1 sorted函数
实现一
- sorted函数对流进行自然排序,其中的元素必须实现Comparable接口
先构造一个实现了Comparable接口的类
public class Student implements Comparable<Student>{
private String name = null;
private int age = 0;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student student) {
return this.age - student.age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试代码
Student stu1 = new Student("name1", 12);
Student stu2 = new Student("name2", 13);
List<Student> list = new ArrayList<>(2);
list.add(stu2);
list.add(stu1);
System.out.println(list);
List<Student> resultList = list.stream().sorted().collect(Collectors.toList());
System.out.println(resultList);
实现二
sorted(Comparator<? super T> comparator) ⽤用来⾃自定义升降序
Student stu1 = new Student("name1", 12);
Student stu2 = new Student("name2", 13);
List<Student> list = new ArrayList<>(2);
list.add(stu2);
list.add(stu1);
// 法一:
// List<Student> resultList = list.stream().sorted(Comparator.comparing(stu -> stu.getAge())).collect(Collectors.toList());
// 法二
// Comparator.reverseOrder() 表示反序
List<Student> resultList = list.stream().sorted(Comparator.comparing(stu -> stu.getAge(), Comparator.reverseOrder())).collect(Collectors.toList());
System.out.println(resultList);
3.2 limit函数
截断流使其最多只包含指定数量的元素
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
List<String> resultList = list.stream().limit(3).collect(Collectors.toList());
System.out.println(resultList);
4. allMatch与anyMatch
4.1allMatch函数
检查是否匹配所有元素,只有全部符合才返回true
List<String> list = new ArrayList<>();
list.add("1");
list.add("22");
list.add("333");
list.add("4444");
boolean result = list.stream().allMatch(str -> str.length() < 3);
// false
System.out.println(result);
4.2 anyMatch
检查是否匹配所有元素,只有全部符合才返回trueanyMatch函数
List<String> list = new ArrayList<>();
list.add("1");
list.add("22");
list.add("333");
list.add("4444");
boolean result = list.stream().anyMatch(str -> str.length() < 3);
// true
System.out.println(result);
5. max和min函数
最⼤值和最小值
Student stu1 = new Student("name1", 12);
Student stu2 = new Student("name2", 13);
Student stu3 = new Student("name3", 14);
List<Student> list = new ArrayList<>(3);
list.add(stu2);
list.add(stu1);
list.add(stu3);
Optional<Student> max = list.stream().max(Comparator.comparing(stu -> stu.getAge()));
Optional<Student> min = list.stream().min((stud1, stud2) -> Integer.compare(stud1.getAge(), stud2.getAge()));
System.out.println(max);
System.out.println(min);
6. 并行流paralleStream
6.1 为什么会有并行流
集合做重复的操作,如果使用串行执行会相当耗时,因此一般会采用多线程来加快,java8的paralleStream用fork/join框架提供了并发执行的能力
6.2 底层原理
- 线程池(ForkJoinPool)维护一个线程队列
- 可以分割任务,将父任务拆分成子任务,完全贴合分治思想
6.3 两个区别
List<String> list = new ArrayList<String>();
list.add("1");
list.add("22");
list.add("333");
list.add("4444");
// 乱序输出
list.parallelStream().forEach(str -> System.out.println(str));
System.out.println("------------------------------------");
// 顺序输出
list.stream().forEach(str -> System.out.println(str));
6.4 问题
- paralleStream并行是否一定比Stream串行快
不是,数据量少的情况,可能串行更快,ForkJoin会消耗性能。
下例我们尝试检测性能差距
List<String> list = new ArrayList<String>(1000000);
for(int i = 0; i < 1000000; i++) {
list.add(String.valueOf(i));
}
/*
long streamStartTime = System.currentTimeMillis();
list.stream().forEach(str -> System.out.println(str));
long streamEndTime = System.currentTimeMillis();
// 耗时3589毫秒
System.out.println(streamEndTime - streamStartTime);
*/
long paralleStreamStartTime = System.currentTimeMillis();
list.stream().forEach(str -> System.out.println(str));
long paralleStreamEndTime = System.currentTimeMillis();
// 耗时3512
System.out.println(paralleStreamEndTime - paralleStreamStartTime);
可以看出性能差距并不明显,并行流在实际中是否有用还需观察
- 是否可都用并行
不行,部分情况会有线程安全问题,paralleStream里面使用的外部变量,比如集合一定要使用线程安全集合,不然就会引发多线程安全问题
7. reduce操作
7.1 简介
- 聚合操作,中文意思是减少
- 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
7.2 常用方法
方法一
Optional<T> reduce(BinaryOperator<T> accumulator);
实例:
将集合中所有元素求和输出
List<Integer> list = new ArrayList<>(4);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.stream().reduce((item1, item2) -> item1 + item2).get());
常用方法二
T reduce(T identity, BinaryOperator<T> accumulator);
- identity用户提供一个循环计算的初始值
- accumulator计算的累加器
实例:
100作为初始值,然后和第一个元素相加,结果在和第二个元素相加,知道全部相加完成
List<Integer> list = new ArrayList<>(4);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.stream().reduce(100 ,(sum, item2) -> sum + item2));
7.3 综合实例
求数组中元素的最大值
List<Integer> list = new ArrayList<>(4);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.stream().reduce((item1, item2) -> item1 > item2 ? item1 : item2).get());
8. forEach操作
8.1 简介
jdk8里面新增的接口
8.2 实例操作
List<Integer> list = new ArrayList<>(4);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream().forEach(item -> System.out.println(item));
8.3 注意点
- 不能修改包含外部变量的值
- 不能用break或者return或者continue等关键字结束或跳过循环
9 collector收集器
9.1 collect()方法的作用
- 一个终端操作,对于流中的数据进行归集操作,collect方法接受的参数是一个Collector
- 有两个重载方法,在Stream接口里面
9.2 Collector的作用
就是收集器,也是一个接口,它的工具类Collectors提供了很多工厂方法
9.2 Collectors的作用
工具类,提供了很多常见的收集器实现
- Collectors.toList()
- Collectors.toMap()
- Collectors.toSet()
- Collectors.toCollection():用自定义的实现Collection的数据结构收集
- Collectors.toCollection(LinkedList::new)
- Collectors.toCollection(CopyOnWriteArrayList::new)
- Collectors.toCollection(TreeSet::new)
10. 收集器joining函数
拼接函数Collectors.joining
10.1 三种重载方法
Collectors.joining()
Collectors.joing("param")
Collectors.joing("param1","param2","param3")
10.2 实例:
List<String> list = new ArrayList<>(4);
list.add("1");
list.add("2");
list.add("3");
list.add("4");
String result1 = list.stream().collect(Collectors.joining());
// 输出1234
System.out.println(result1);
String result2 = list.stream().collect(Collectors.joining(","));
// 输出1,2,3,4
System.out.println(result2);
String result3 = list.stream().collect(Collectors.joining(",", "[", "]"));
// 输出[1,2,3,4]
System.out.println(result3);
11.收集器partitioningBy分组
11.1 简介
讲解收集器partitioningBy分组
Collectors.partitioningBy分组,key是boolean类型
11.2 实例
对list进行分组,字符串长度大于3的为一组,其他为另外一组
List<String> list = new ArrayList<>(4);
list.add("1");
list.add("22");
list.add("333");
list.add("4444");
Map<Boolean, List<String>> result = list.stream().collect(Collectors.partitioningBy(str -> str.length() > 3));
// 输出{false=[1, 22, 333], true=[4444]}
System.out.println(result);
12.收集器groupingBy分组
分组统计:
- 聚合函数进行统计查询,分组后统计个数
- Collectors.counting()统计元素个数
- 底层也是HashMap实现,所以分组的依据是equals和hashCode
实例代码:
Student stu1 = new Student("name1", 12);
Student stu2 = new Student("name2", 13);
Student stu3 = new Student("name3", 14);
List<Student> list = new ArrayList<>(3);
list.add(stu2);
list.add(stu1);
list.add(stu1);
list.add(stu3);
Map<String, List<Student>> resultMap1 = list.stream().collect(Collectors.groupingBy(Student::getName));
Map<String, Long> resultMap2 = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
// 输出{name3=[Student{name='name3', age=14}], name2=[Student{name='name2', age=13}], name1=[Student{name='name1', age=12}, Student{name='name1', age=12}]}
System.out.println(resultMap1);
// 输出{name3=1, name2=1, name1=2}
System.out.println(resultMap2);
13. summarizing集合统计
作用:可以一个方法把统计相关的基本上都完成
分类:
- summarizingInt
- summarizingLong
- summarizingDouble
实例:统计学生的各个年龄信息
Student stu1 = new Student("name1", 12);
Student stu2 = new Student("name2", 13);
Student stu3 = new Student("name3", 14);
List<Student> list = new ArrayList<>(3);
list.add(stu2);
list.add(stu1);
list.add(stu1);
list.add(stu3);
DoubleSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("平均值" + summaryStatistics.getAverage());
System.out.println("最大值" + summaryStatistics.getMax());
System.out.println("最小值" + summaryStatistics.getMin());
System.out.println("人数" + summaryStatistics.getCount());
System.out.println("总和" + summaryStatistics.getSum());
输出结果
平均值12.75
最大值14.0
最小值12.0
人数4
总和51.0
14. count
count() 方法计算给定Stream里包含多少个对象
List<String> list = new ArrayList<>(3);
list.add("test1");
list.add("test2");
list.add("tes");
long count = list.stream().filter(str -> str.startsWith("test")).count();
// 输出2
System.out.println(count);
15. Stream.of
Stream.of操作可以生成流
Stream<Integer> integerStream = Stream.of(11, 33, 44);