Stream API

Stream API

StreamAPI(java.util.stream)把真正的函数式编程风格引入到Java中,这是目前为止对Java类库最好的补充

StreamAPI实现将数据在后端去操作


StreamAPI和集合框架的区别

  • Stream API关注的是数据的计算(排序、查找、过滤、映射、遍历),面向CPU
  • 集合关注的是数据的存储,面向内存的
  • Stream API之于集合,类似于SQL之余数据表的查询

使用说明

  • Stream自己不会存储元素
  • Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream
  • Stream操作时延迟进行的,这意味着它们会等到需要结果的时候才执。一旦中途执行中止操作,就执行中间操作链,并产生结果
  • Stream一旦执行了终止操作,就不能再调用其他中间操作或终止操作了

Stream执行流程

  • Steam的实例化

  • 一系列的中间操作

  • 执行中止操作


创建Stream

创建Stream方式一:通过集合

结构Collection中提供了一个stream方法,可通过集合获取顺序流和并行流

1
2
3
4
5
6
7
8
9
10
@Test
public void test(){
//创建Stream方式一:通过集合
//结构Collection中提供了一个stream方法,可通过集合获取顺序流和并行流
List list = new ArrayList();
//获取一个顺序流
list.stream();
//获取一个并行流
list.parallelStream();
}

创建Stream方式二:通过数组

调用Arrays类的static <T> Stream <T> stream(T[] array):返回一个流

1
2
3
4
5
6
7
8
@Test
public void test1(){
Integer[] arr = new Integer[]{1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
//基本数据类型不能当作泛型使用,所以有专门的基本数据类型Stream
int[] ints = {1, 2, 3, 4, 5};
IntStream stream1 = Arrays.stream(ints);
}

创建Stream方式三:通过Stream的of()
1
2
3
4
@Test
public void test3() {
Stream<String> stream = Stream.of("AA", "BB", "CC");
}

Stream的中间操作(方法)

在此我们建立一个实体类Employee

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class Employee {
private int id;
private String name;
private int age;
private double salary;

public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

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 double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}

在此我们建立一个数据集合类EmployeeDate,此类中有get方法返回一个Employee类型的List集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;
import java.util.List;

public class EmployeeDate {
public static List<Employee> getEmployees(){
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001,"马化腾",34,6000.38));
list.add(new Employee(1002,"马云",2,9876.12));
list.add(new Employee(1003,"刘强东",33,3000.82));
list.add(new Employee(1004,"雷军",26,7657.37));
list.add(new Employee(1005,"李彦宏",65,5555.32));
list.add(new Employee(1005,"比尔盖茨",42,9500.43));
list.add(new Employee(1006,"任正非",26,4333.32));
list.add(new Employee(1007,"扎克伯格",35,2500.32));
return list;
}
}
筛选与切片
方法名 说明
filter() 过滤
limit() 截取
skip() 跳过
distinct() 筛选(去重)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Test
public void test() {
//练习:查询员工表中薪资大于7000的员工信息
List<Employee> list = EmployeeDate.getEmployees();
Stream<Employee> stream = list.stream();

//使用流过滤工资(salary)大于七千的,并终止操作(输出到控制台)
stream.filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);

System.out.println();
//终止操作之后,此Stream将无法再进行任何中间操作

//单独的流执行截取前四段,并终止操作(输出到控制台)
list.stream().limit(4).forEach(System.out::println);

//相同的,如果没有进行终止操作,可以进行多步中间操作(方法链)
list.stream().filter(employee -> employee.getSalary() > 7000).limit(2).forEach(System.out::println);

//跳过前五个
list.stream().skip(5).forEach(System.out::println);

//筛选(去重)
list.add(new Employee(1009, "马斯克", 40, 12500.32));
list.add(new Employee(1009, "马斯克", 40, 12500.32));
list.add(new Employee(1009, "马斯克", 40, 12500.32));
list.add(new Employee(1009, "马斯克", 40, 12500.32));
list.stream().distinct().forEach(System.out::println);
}

映射
方法名 说明
map(Function f) 接受一个函数作为参数,将元素转换为其他形式或提取信息

该方法会被应用到每一个元素上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test1() {
//练习:转大写
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::println);
System.out.println();
//练习:获取员工姓名大于3的员工的姓名
List<Employee> list1 = EmployeeDate.getEmployees();
//方式1:先过滤出名字大于3的再提取名字
list1.stream().filter(employee -> employee.getName().length() > 3).map(employee -> employee.getName()).forEach(System.out::println);
//方式2:先提取所有名字,再去找长度大于三的
list1.stream().map(employee -> employee.getName()).filter(name -> name.length() > 3).forEach(System.out::println);
//方式3:使用方法引用
list1.stream().filter(employee -> employee.getName().length() > 3).map(Employee::getName).forEach(System.out::println);
}

排序
方法名 说明
sorted() 自然排序(默认升序)
sorted(Comparator com) 定制排序(默认升序)
sorted((s1,s2)->-s1.compareTo(s2)) 降序排列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    @Test
public void test2(){
Integer[] integers = {345, 3, 64, 3, 45, 7, 3, 34, 65, 68};
//使用方式2构建stream流
Arrays.stream(integers).sorted().forEach(System.out::println);

//字符数组也可以实现自然排序
String[] strings = {"GG", "MM", "SS", "JJ", "DD"};
Arrays.stream(strings).sorted().forEach(System.out::println);
//降序排列
Arrays.stream(strings).sorted(String::compareTo).forEach(System.out::println);

//实体类对象不能实现自然排序,因为没有实现自然排序接口
// List<Employee> list = EmployeeDate.getEmployees();
// list.stream().sorted().forEach(System.out::println);

//定制排序:sorted(Comparator com)
List<Employee> list = EmployeeDate.getEmployees();
list.stream().sorted((e1,e2)->e1.getAge() - e2.getAge()).forEach(System.out::println);
}

Stream的终止操作

匹配与查找
方法名 说明
allMatch() 全部元素匹配条件才为true
anyMatch() 有一个元素匹配条件就为true
findFirst() 返回第一个元素
1
2
3
4
5
6
7
8
9
10
@Test
public void test4(){
//查询集合里的员工是否全都大于18岁
List<Employee> list = EmployeeDate.getEmployees();
System.out.println(list.stream().allMatch(employee -> employee.getAge() > 18));
//查询集合里的员工是否存在大于18岁
System.out.println(list.stream().anyMatch(employee -> employee.getAge() > 18));
//返回第一个元素
System.out.println(list.stream().findFirst());
}

方法名 说明
count() 返回流中元素的个数
max(Comparator c) 返回最高的
min(Comparator c) 返回最低的
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test5(){
//返回流中元素个数
List<Employee> list = EmployeeDate.getEmployees();
System.out.println(list.stream().count());
//返回最高的工资的员工
System.out.println(list.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
//返回最高工资
//方式一
System.out.println(list.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).get().getSalary());
//方式二
System.out.println(list.stream().map(employee -> employee.getSalary()).max(Double::compare).get());
}

返回最低工资同理


方法名 说明
forEach(Consumer c) 内部迭代
1
2
3
4
5
6
@Test
public void test(){
List<Employee> list = EmployeeDate.getEmployees();
//内部迭代
list.stream().forEach(System.out::println);
}

list集合的遍历方式

  • 使用Iterator
  • 增强for
  • 一般for
  • forEach**(新增)**
1
2
//针对于集合,jdk8中增加了一个遍历的方法
list.forEach(System.out::println);

归约
方法名 说明
reduce(T identity, BinaryOperator) 可以将流中元素反复结合起来,得到一个值,返回T
reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值,返回Optional<T>
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test2() {
//练习1:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
System.out.println(list.stream().reduce(0, Integer::sum));//方法引用

//求所有员工的工资和
List<Employee> list1 = EmployeeDate.getEmployees();
System.out.println(list1.stream().map(employee -> employee.getSalary()).reduce((salary1, salary2) -> Double.sum(salary1, salary2)));
System.out.println(list1.stream().map(employee -> employee.getSalary()).reduce(Double::sum));
}

收集
方法名 说明
collect(Collector c) 将流转换为其他形式,接受一个collector接口的实现。用于给Stream中的元素做汇总的方法

另外Collector类给提供了很多静态方法,可供作为collect方法的参数使用


Stream API
http://blog.170827.xyz/2024/03/23/Stream API/
作者
XIAOBAI
发布于
2024年3月23日
许可协议