一、概述

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 StreamAPI 对集合数据进行操作,就类似于使用 SQL 执行的数据路查询

官方文档对它的介绍如下:

1
A sequence of elements supporting sequential and parallel aggregate operations.

大概意思为:

1
支持顺序和并行聚合操作的元素序列。

可以看出 Stream

  • 是一个元素序列
  • 支持对元素的串行和并行操作
  • 所有对流的操作并不会改变原集合的值

那么串行操作和并行操作有什么区别呢?

我的理解是:

  • 串行操作类似于单线程,处理数据时是一条一条处理的,所以处理的数据自然也是有序的
  • 并行操作类似于多线程,多条数据一起处理,虽然处理时效率较高,那么处理时数据自然也是无序的

总体来说,使用 Stream 的基本步骤为:

  1. 生成 Stream 操作(创建操作)
  2. 中间操作(转换操作)
  3. 终结操作(聚合操作)

这里我以 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流
Stream<Student> studentStream = studentList.parallelStream();//返回并行Stream流
}

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();

//方式一:直接使用Collection接口的ParallelStream()创建并行流
Stream<Student> parallelStream = list.parallelStream();
System.out.println(parallelStream.isParallel());

//方式二:使用BaseStream接口的Parallel()方法将串行流转变为并行流
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) //2, 2, 6, 5, 4, 3
.distinct(); // 2, 6, 5, 4, 3
}

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) // 1, 2, 2, 6
.skip(2); // 2, 6
}

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() // 1, 2, 2, 3, 4, 5, 6
.sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}); // 6, 5, 4, 3, 2, 2, 1
}

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)) //将所有同学的年龄设成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));
}

//输出true
  • 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));
}

//输出false
  • 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));
}

//输出true
  • 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. 规约操作

  • count:返回流中元素个数
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 集合