一、概述
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 StreamAPI 对集合数据进行操作,就类似于使用 SQL 执行的数据路查询
官方文档对它的介绍如下:
1
| A sequence of elements supporting sequential and parallel aggregate operations.
|
大概意思为:
可以看出 Stream
- 是一个元素序列
- 支持对元素的串行和并行操作
- 所有对流的操作并不会改变原集合的值
那么串行操作和并行操作有什么区别呢?
我的理解是:
- 串行操作类似于单线程,处理数据时是一条一条处理的,所以处理的数据自然也是有序的
- 并行操作类似于多线程,多条数据一起处理,虽然处理时效率较高,那么处理时数据自然也是无序的
总体来说,使用 Stream 的基本步骤为:
- 生成 Stream 操作(创建操作)
- 中间操作(转换操作)
- 终结操作(聚合操作)
这里我以 Student 类来举例具体操作(getter、setter、constructor、toString方法略)
1 2 3 4 5 6
| public class Student { private String name; private Integer age; private Character sex; private String teacher; }
|
定义返回数据的工具类
1 2 3 4 5 6 7 8 9 10 11
| public class StudentUtils { public static List<Student> getStudentList() { Student student1 = new Student("张三", 18, '男', "马云"); Student student2 = new Student("李四", 20, '女', "马云"); Student student3 = new Student("王五", 20, '女', "马化腾"); Student student4 = new Student("赵六", 40, '女', "马云"); Student student5 = new Student("钱七", 30, '女', "王健林");
return Arrays.asList(student1, student2, student3, student4, student5); } }
|
二、Stream 的创建
1. 通过集合创建 Stream
函数原型:
1 2 3 4 5 6 7 8
| default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
|
第一个为返回一个串行的 Stream 流,第二个返回一个并行的 Stream 流
使用举例:
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> studentList = StudentUtils.getStudentList();
Stream<Student> stream = studentList.stream(); Stream<Student> studentStream = studentList.parallelStream(); }
|
2. 通过数组创建 Stream
通过 Arrays 类的 Stream 方法,函数原型为:
1 2 3
| public static <T> Stream<T> stream(T[] array) { return stream(array, 0, array.length); }
|
使用举例:
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> studentList = StudentUtils.getStudentList(); Student[] students = studentList.toArray(new Student[0]);
Stream<Student> stream = Arrays.stream(students); }
|
3. 通过 Stream 类的 of 方法来创建
函数原型:
1 2 3
| public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
|
使用举例:
1 2 3
| public static void main(String[] args) { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); }
|
此处 Stream 自动把 1,2,3 这些数字封装成了对应的包装类 Integer
4. 创建 Stream 无限流
通过 Stream 类的 iterate 方法和 generate 方法来创建无限流
iterate函数定义(它包含一个重载的方法,两参数的和三参数的):
1 2 3
| public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
|
iterate使用举例:
1 2 3
| public static void main(String[] args) { Stream<Integer> stream1 = Stream.iterate(0, x -> x + 1); }
|
第一个参数为seed(种子值),根据种子生成第一个x的值并放到流中。第二个参数是一个lambda表达式,本式的意思是每次让x加1并把结果放入流中
1 2 3 4 5
| public static void main(String[] args) { Stream<Integer> stream2 = Stream.iterate(0, x -> { return x < 100; } ,x -> x + 1); }
|
第一个参数为seed(种子值),根据种子生成第一个x的值并放到流中。第二个参数为一个lambda表达式,返回boolean类型值,若值为真,则根据第三个参数继续生成流;若值为假,则从此刻停止生成流
generate函数定义:
1
| public static<T> Stream<T> generate(Supplier<? extends T> s)
|
generate使用举例:
1 2 3 4
| public static void main(String[] args) { Stream<Integer> integerStream = Stream.generate(() -> new Random().nextInt()); Stream<Boolean> booleanStream = Stream.generate(() -> new Random().nextBoolean()); }
|
5. 创建 Stream 并行流
创建并行流有两种方式,一是直接在集合的基础上创建并行流,二是在串行流的基础上创建并行流
1 2 3 4 5 6 7 8 9 10 11 12
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
Stream<Student> parallelStream = list.parallelStream(); System.out.println(parallelStream.isParallel());
Stream<Student> stream = list.stream(); Stream<Student> parallelStream2 = stream.parallel(); System.out.println(parallelStream2.isParallel()); }
|
三、Stream 的中间操作
Stream常见的中间操作
方法名 |
参数类型 |
方法说明 |
filter |
Predicate |
用于对流中的数据进行过滤 |
concat |
Stream, Stream |
合并两个流为一个流 |
distinct |
|
通过流中元素的 hashcode 去除重复元素 |
sorted |
|
将流中元素自然排序 |
sorted |
Comparator |
将流中元素根据排序器进行排序 |
map |
Function |
可以得到流中每一个元素,将流中元素进行映射,返回映射结果组成的流 |
peek |
Consumer |
可以得到流中每一个元素,然后对元素进行操作,无返回值 |
limit |
long |
截取前n个元素 |
skip |
long |
与limit相反,跳过前n个元素,返回后面的元素 |
这几种常见的中间操作大致也可归为几类:
1. 过滤操作
- filter:用于对流中的数据进行过滤
- distinct:通过流中元素的 hashcode 去除重复元素
1 2 3 4 5 6
| public static void main(String[] args) { Stream<Integer> stream = Stream.of(1, 2, 2, 6, 5, 4, 3); Stream<Integer> result = stream .filter(n -> n > 1) .distinct(); }
|
2. 截取操作
- limit:获取前n个元素
- skip:跳过前n个元素,获取后面的元素
1 2 3 4 5 6
| public static void main(String[] args) { Stream<Integer> stream = Stream.of(1, 2, 2, 6, 5, 4, 3); Stream<Integer> result = stream .limit(4) .skip(2); }
|
3. 排序操作
- sorted:进行自然排序
- sorted(Comparator):按照比较器进行排序
1 2 3 4 5 6 7 8 9 10 11
| public static void main(String[] args) { Stream<Integer> stream = Stream.of(1, 2, 2, 6, 5, 4, 3); Stream<Integer> result = stream .sorted() .sorted(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); }
|
4. 映射操作
- map:可以得到流中每一个元素,将流中元素进行映射,返回映射结果组成的流
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList(); list.stream() .map(Student::getName) .forEach(System.out::println); }
|
5. 消费操作
- peek:可以得到流中每一个元素,然后对元素进行操作,无返回值
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList(); list.stream() .peek(student -> student.setAge(10)) .forEach(System.out::println); }
|
四、Stream 终结操作
1. 匹配、聚合操作
- allMatch:对流中所有元素进行判断,如果都满足条件返回true,否则返回false
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList(); System.out.println(list.stream().allMatch(student -> student.getAge() > 10)); }
|
- noneMatch:对流中所有元素进行判断,如果都不满足条件返回true,否则返回false
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList(); System.out.println(list.stream().noneMatch(student -> student.getAge() > 19)); }
|
- anyMatch:对流中所有元素进行判断,只要有一个满足条件的就返回true,否则返回false
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList(); System.out.println(list.stream().anyMatch(student -> student.getAge() > 30)); }
|
- findFirst:函数返回值为 Optional 类型。如果流为空,返回一个空 Optional,否则返回第一个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
System.out.println("=======串行流"); Stream<Student> stream = list.stream(); System.out.println(stream.findFirst());
System.out.println("=======并行流"); Stream<Student> parallel = list.parallelStream(); System.out.println(parallel.findFirst()); }
=======串行流 Optional[Student{name='张三', age=18, sex=男, teacher='马云'}] =======并行流 Optional[Student{name='张三', age=18, sex=男, teacher='马云'}]
|
- findAny:如果数据较少,并且串行的情况下一般还是会返回第一个元素,但是当并行的情况下一般会返回任何一个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
System.out.println("=======串行流"); Stream<Student> stream = list.stream(); System.out.println(stream.findAny());
System.out.println("=======并行流"); Stream<Student> parallel = list.parallelStream(); System.out.println(parallel.findAny()); }
=======串行流 Optional[Student{name='张三', age=18, sex=男, teacher='马云'}] =======并行流 Optional[Student{name='王五', age=20, sex=女, teacher='马化腾'}]
|
2. 规约操作
1 2 3 4 5 6
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
Stream<Student> stream = list.stream(); System.out.println(stream.count()); }
|
- max:参数为 Comparator 类型的比较器,返回值为 Optional 类型。返回比较器比较结果的最后一个元素的 Optional 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
Stream<Student> stream = list.stream(); System.out.println(stream.max(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o2.getAge() - o1.getAge(); } })); }
Optional[Student{name='张三', age=18, sex=男, teacher='马云'}]
|
- min:参数为 Comparator 类型的比较器,返回值为 Optional 类型。返回比较器比较结果的第一个元素的 Optional 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) { List<Student> list = StudentUtils.getStudentList();
Stream<Student> stream = list.stream(); System.out.println(stream.min(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o2.getAge() - o1.getAge(); } })); }
Optional[Student{name='赵六', age=40, sex=女, teacher='马云'}]
|
3. 收集操作
这个就比较简单了,主要借助 Collectors
Collectors.toList:把 Stream 流转化为 List 集合
Collectors.toSet:把 Stream 流转化为 Set 集合
Collectors.toMap:把 Stream 流转化为 Map 集合