网站公告

本次更新:没啥更新的

“钱塘江上潮信起,今日方知我是我”

Skip to content

Stream 终止操作 ​

字数: 0 字 时长: 0 分钟

1. 概述 ​

终结操作用于结束 Stream 的处理流程,并得到最终结果。

常见终结操作:

text
forEach
count
max / min
anyMatch / allMatch / noneMatch
findAny / findFirst
reduce
collect

Stream 的基本执行过程:

text
数据源
  ↓
创建 Stream
  ↓
中间操作
  ↓
终结操作
  ↓
得到最终结果

终结操作会触发前面的中间操作真正执行。

一个 Stream 执行终结操作之后,就不能再次使用。


2. forEach() ​

forEach() 用于遍历 Stream 中的元素,并对每个元素执行指定操作。

例如:输出所有作者的姓名,并去重。

java
List<Author> authors = getAuthors();

authors.stream()
        .map(author -> author.getName())
        .distinct()
        .forEach(name -> System.out.println(name));

可以理解为:

text
Stream 中的元素
      ↓
逐个取出
      ↓
执行指定操作

3. count() ​

count() 用于统计当前 Stream 中元素的数量。

返回值类型:

java
long

例如:统计作者一共出版了多少本不同的书。

java
List<Author> authors = getAuthors();

long count = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .distinct()
        .count();

System.out.println(count);

4. max() 和 min() ​

max() 和 min() 分别用于获取 Stream 中的最大值和最小值。

由于 Stream 可能为空,所以返回值使用:

java
Optional<T>

4.1 max() ​

获取所有书籍中的最高评分:

java
List<Author> authors = getAuthors();

Optional<Integer> max = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .map(book -> book.getScore())
        .max((o1, o2) -> o1 - o2);

max.ifPresent(score -> System.out.println(score));

4.2 min() ​

获取所有书籍中的最低评分:

java
List<Author> authors = getAuthors();

Optional<Integer> min = authors.stream()
        .flatMap(author -> author.getBooks().stream())
        .map(book -> book.getScore())
        .min((o1, o2) -> o1 - o2);

min.ifPresent(score -> System.out.println(score));

5. 查找和匹配 ​

Stream 提供了一组用于判断和查找元素的终结操作:

text
anyMatch()
allMatch()
noneMatch()
findAny()
findFirst()

5.1 anyMatch() ​

anyMatch() 用于判断:

是否至少有一个元素满足条件。

只要有一个元素满足条件,就返回 true。

例如:判断是否存在年龄大于 29 岁的作者。

匿名内部类写法:

java
List<Author> authors = getAuthors();

boolean result = authors.stream()
        .anyMatch(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() > 29;
            }
        });

System.out.println(result);

Lambda 写法:

java
List<Author> authors = getAuthors();

boolean result = authors.stream()
        .anyMatch(author -> author.getAge() > 29);

System.out.println(result);

5.2 allMatch() ​

allMatch() 用于判断:

是否所有元素都满足条件。

所有元素都满足条件时返回 true,否则返回 false。

例如:判断所有作者是否都已经成年。

java
List<Author> authors = getAuthors();

boolean result = authors.stream()
        .allMatch(author -> author.getAge() > 18);

System.out.println(result);

5.3 noneMatch() ​

noneMatch() 用于判断:

是否没有任何元素满足条件。

如果所有元素都不满足指定条件,则返回 true。

例如:判断是否没有作者超过 100 岁。

java
List<Author> authors = getAuthors();

boolean result = authors.stream()
        .noneMatch(author -> author.getAge() > 100);

System.out.println(result);

三个匹配方法可以简单记成:

text
anyMatch()
    ↓
至少一个满足

allMatch()
    ↓
全部满足

noneMatch()
    ↓
一个都不满足

5.4 findAny() ​

findAny() 用于获取 Stream 中的任意一个元素。

返回值:

java
Optional<T>

它不能保证获取到的一定是第一个元素。

例如:获取任意一个年龄大于 18 岁的作者。

java
List<Author> authors = getAuthors();

Optional<Author> any = authors.stream()
        .filter(author -> author.getAge() > 18)
        .findAny();

any.ifPresent(author -> System.out.println(author.getName()));

5.5 findFirst() ​

findFirst() 用于获取 Stream 中的第一个元素。

返回值:

java
Optional<T>

例如:获取年龄最小的作者,并打印姓名。

java
List<Author> authors = getAuthors();

Optional<Author> first = authors.stream()
        .sorted((o1, o2) -> o1.getAge().compareTo(o2.getAge()))
        .findFirst();

first.ifPresent(author -> System.out.println(author.getName()));

这里先按照年龄升序排序:

text
年龄最小
   ↓
年龄较大

然后通过 findFirst() 获取第一个元素。


6. reduce() ​

reduce() 用于按照指定的计算规则,将 Stream 中的多个元素最终合并成一个结果。

可以理解为:

text
多个元素
   ↓
不断进行计算
   ↓
一个结果

例如:

text
1、2、3、4、5
      ↓
     求和
      ↓
     15

6.1 两个参数的 reduce() ​

常见形式:

java
reduce(identity, accumulator)

其中:

  • identity:初始值
  • accumulator:计算规则

内部执行逻辑可以简单理解为:

java
T result = identity;

for (T element : stream) {
    result = accumulator.apply(result, element);
}

return result;

传统循环求和:

java
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int result = 0;

for (int i : arr) {
    result = result + i;
}

System.out.println(result);

这里的:

java
0

就类似 reduce() 中的 identity。


6.2 求所有作者年龄之和 ​

匿名内部类写法:

java
List<Author> authors = getAuthors();

Integer result = authors.stream()
        .map(author -> author.getAge())
        .reduce(0, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer result, Integer element) {
                return result + element;
            }
        });

System.out.println(result);

Lambda 写法:

java
List<Author> authors = getAuthors();

Integer result = authors.stream()
        .map(author -> author.getAge())
        .reduce(0, (result, element) -> result + element);

System.out.println(result);

执行过程可以理解为:

text
identity = 0

0 + 第一个年龄
      ↓
结果 + 第二个年龄
      ↓
结果 + 第三个年龄
      ↓
最终结果

6.3 求最大年龄 ​

使用 Integer.MIN_VALUE 作为初始值:

java
List<Author> authors = getAuthors();

Integer result = authors.stream()
        .map(author -> author.getAge())
        .reduce(
                Integer.MIN_VALUE,
                (result, element) ->
                        result < element ? element : result
        );

System.out.println(result);

每次比较:

text
result
   ↘
    比较 → 保留较大的值
   ↗
element

最终得到最大年龄。


6.4 求最小年龄 ​

使用 Integer.MAX_VALUE 作为初始值:

java
List<Author> authors = getAuthors();

Integer result = authors.stream()
        .map(author -> author.getAge())
        .reduce(
                Integer.MAX_VALUE,
                (result, element) ->
                        result > element ? element : result
        );

System.out.println(result);

每次比较后保留较小的值,最终得到最小年龄。


6.5 一个参数的 reduce() ​

另一种形式:

java
reduce(accumulator)

它没有指定初始值,因此会使用 Stream 中的元素参与第一次计算。

由于 Stream 可能为空,所以返回值为:

java
Optional<T>

内部逻辑可以简单理解为:

java
boolean foundAny = false;
T result = null;

for (T element : stream) {
    if (!foundAny) {
        foundAny = true;
        result = element;
    } else {
        result = accumulator.apply(result, element);
    }
}

return foundAny
        ? Optional.of(result)
        : Optional.empty();

例如求作者最小年龄。

匿名内部类写法:

java
List<Author> authors = getAuthors();

Optional<Integer> result = authors.stream()
        .map(author -> author.getAge())
        .reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer result, Integer element) {
                return result < element ? result : element;
            }
        });

result.ifPresent(age -> System.out.println(age));

Lambda 写法:

java
List<Author> authors = getAuthors();

Optional<Integer> result = authors.stream()
        .map(author -> author.getAge())
        .reduce(
                (result, element) ->
                        result < element ? result : element
        );

result.ifPresent(age -> System.out.println(age));

没有指定初始值的 reduce() 返回 Optional,因为 Stream 可能没有任何元素。


7. collect() ​

collect() 也是 Stream 中非常重要的终结操作,用于将 Stream 中的数据收集成其他数据结构。

常见的收集方式:

java
Collectors.toList()

Collectors.toSet()

Collectors.toMap()

例如:

java
List<String> names = authors.stream()
        .map(author -> author.getName())
        .collect(Collectors.toList());

collect() 和 Collectors 的内容较多,单独整理在:

text
Collectors.md

8. 常用终结操作小结 ​

方法作用
forEach()遍历元素
count()统计元素数量
max()获取最大值
min()获取最小值
anyMatch()判断是否至少一个元素满足条件
allMatch()判断是否所有元素都满足条件
noneMatch()判断是否所有元素都不满足条件
findAny()获取任意一个元素
findFirst()获取第一个元素
reduce()将多个元素归并成一个结果
collect()收集 Stream 中的数据

可以简单记成:

text
forEach
    ↓
遍历

count
    ↓
统计

max / min
    ↓
最值

xxxMatch
    ↓
判断

findXxx
    ↓
查找

reduce
    ↓
归并

collect
    ↓
收集

终结操作执行后,当前 Stream 就已经被消费,不能再次使用。

Noctiflorous Garden · MIT License