Skip to content
章节导航

Stream 的映射、排序、查找、规约

map:映射(重点)

Stream 映射(Mapping)操作用于转换流中的元素,将元素从一种形式转换为另一种形式。将元素通过指定的函数进行转换,生成新的流。

java
// 字符串转大写
List<String> words = Arrays.asList("apple", "banana", "cherry");
words.stream()
    .map(String::toUpperCase)
    .forEach(System.err::println) ;
// 结果: ["APPLE", "BANANA", "CHERRY"]


// 数学计算
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// 结果: [1, 4, 9, 16, 25]

sorted:排序

Stream 排序操作用于对流中的元素进行排序。Java Stream API 提供了灵活的排序方法,支持自然排序和自定义排序。使用元素的自然顺序进行排序(元素必须实现 Comparable 接口)。

1、sorted() - 自然排序

java
// 数字自然排序(升序)
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);
List<Integer> sortedNumbers = numbers.stream()
    .sorted()
    .collect(Collectors.toList());
// 结果: [1, 2, 3, 5, 8, 9]

// 字符串自然排序(字典序)
List<String> words = Arrays.asList("banana", "apple", "cherry", "date");
List<String> sortedWords = words.stream()
    .sorted()
    .collect(Collectors.toList());
// 结果: ["apple", "banana", "cherry", "date"]

2、sorted(Comparator) - 自定义排序

使用指定的比较器进行排序。

java
// 数字降序排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);
List<Integer> descendingNumbers = numbers.stream()
    .sorted(Comparator.reverseOrder())
    .collect(Collectors.toList());
// 结果: [9, 8, 5, 3, 2, 1]

// 字符串按长度排序
List<String> words = Arrays.asList("apple", "kiwi", "banana", "pear");
List<String> byLength = words.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());
// 结果: ["kiwi", "pear", "apple", "banana"]

查找匹配的多种方法

方法使用场景返回值空流结果推荐/替代
anyMatch()检查是否存在满足条件的元素booleanfalse检查存在性首选
allMatch()检查是否所有元素都满足条件booleantrue数据验证、完整性检查
noneMatch()检查是否没有元素满足条件booleantrue黑名单检查、安全性验证
findFirst()获取第一个元素(保持顺序)Optional<T>true需要顺序保证时使用
findAny()获取任意元素(不关心顺序)Optional<T>true并行流中性能更好
count()统计元素数量long0数据分析、统计
max()查找最大值Optional<T>empty需要自定义比较器
min()查找最小值Optional<T>empty需要自定义比较器

1、anyMatch:检查流中是否至少有一个元素满足给定的条件

终端操作,检查流中是否至少有一个元素满足给定的条件

方法签名

java
boolean anyMatch(Predicate<? super T> predicate)

返回值

  • true:如果至少有一个元素满足条件(Predicate 返回 true)
  • false:如果没有任何元素满足条件
  • 如果流为空,返回 false

代码示例

java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 检查是否有偶数
boolean hasEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);
// 结果: true(因为2、4是偶数)

// 检查是否有大于10的数
boolean hasGreaterThanTen = numbers.stream()
    .anyMatch(n -> n > 10);
// 结果: false(所有数都小于等于5)


List<String> words = Arrays.asList("apple", "banana", "cherry", "date");

// 检查是否有以 "a" 开头的单词
boolean startsWithA = words.stream()
    .anyMatch(word -> word.startsWith("a"));
// 结果: true("apple")

// 检查是否有长度大于10的单词
boolean hasLongWord = words.stream()
    .anyMatch(word -> word.length() > 10);
// 结果: false


// 检查用户是否有任何管理员权限
boolean hasAdminPermission = user.getPermissions().stream()
    .anyMatch(permission -> permission.getType() == PermissionType.ADMIN);

// 检查用户是否能访问特定资源
boolean canAccessResource = user.getRoles().stream()
    .flatMap(role -> role.getPermissions().stream())
    .anyMatch(permission -> permission.getResourceId().equals(resourceId));
    
// 检查购物车中是否有促销商品
boolean hasPromotionalItem = cart.getItems().stream()
    .anyMatch(item -> item.isPromotional() && item.getDiscount() > 0);

// 检查申请单中是否有高风险项目
boolean hasHighRisk = application.getProjects().stream()
    .anyMatch(project -> 
        project.getRiskLevel() == RiskLevel.HIGH ||
        project.getBudget() > 1_000_000
    );

2、allMatch:查流中是否所有元素都满足给定的条件

终端操作,用于检查流中是否所有元素都满足给定的条件。

方法签名

java
boolean allMatch(Predicate<? super T> predicate)

返回值

  • true:如果所有元素都满足条件(或流为空)
  • false:如果至少有一个元素不满足条件

示例代码

java
// 检查是否所有数字都是偶数
List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
// 结果: true(所有数字都是偶数)

3、noneMatch:检查流中是否没有任何元素满足给定的条件

终端操作,用于检查流中是否没有任何元素满足给定的条件。

方法签名

java
boolean noneMatch(Predicate<? super T> predicate)

返回值

  • true:如果没有任何元素满足条件(或流为空)
  • false:如果至少有一个元素满足条件

示例代码

java
// 检查是否没有奇数
List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
boolean noOddNumbers = numbers.stream().noneMatch(n -> n % 2 != 0);
// 结果: true(所有数字都是偶数,没有奇数)

// 检查是否没有负数
List<Integer> mixedNumbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noNegatives = mixedNumbers.stream().noneMatch(n -> n < 0);
// 结果: true(所有数字都 >= 0)

// 检查是否没有大于10的数字
boolean noneGreaterThanTen = mixedNumbers.stream().noneMatch(n -> n > 10);
// 结果: true

4、findFirst:获取流中的第一个元素

终端操作,用于获取流中的第一个元素(如果存在的话)。

方法签名

java
Optional<T> findFirst()

返回值

Optional<T>:包含第一个元素的 Optional,如果流为空则返回空的 Optional

示例代码

java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);

// 获取第一个元素
Optional<Integer> first = numbers.stream().findFirst();
// 结果: Optional[3] (第一个元素是3)

// 处理Optional结果
first.ifPresent(n -> System.out.println("第一个数字是: " + n));
// 输出: 第一个数字是: 3

// 获取默认值
Integer result = first.orElse(0);
// 结果: 3

List<String> emptyList = Collections.emptyList();
Optional<String> emptyFirst = emptyList.stream().findFirst();
// 结果: Optional.empty

5、findAny:获取流中的任意一个元素

终端操作,用于获取流中的任意一个元素(如果存在的话)。

方法签名

java
Optional<T> findAny()

返回值

Optional<T>:包含任意元素的 Optional,如果流为空则返回空的 Optional

示例代码

java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);

// 获取任意一个元素
Optional<Integer> any = numbers.stream()
    .findAny();
// 结果: 通常返回 Optional[3](第一个元素),但不保证

// 处理Optional结果
any.ifPresent(n -> System.out.println("找到一个数字: " + n));
// 可能输出: 找到一个数字: 3

List<String> emptyList = Collections.emptyList();
Optional<String> emptyAny = emptyList.stream()
    .findAny();
// 结果: Optional.empty

6、count:统计流中元素的数量

终端操作,用于统计流中元素的数量。

方法签名

java
long count()

返回值

  • long:流中元素的数量
  • 如果流为空,返回 0

示例代码

java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 统计元素总数
long count = numbers.stream()
    .count();
// 结果: 5

List<String> emptyList = Collections.emptyList();
long emptyCount = emptyList.stream()
    .count();
// 结果: 0

// 数组转流计数
int[] array = {1, 2, 3, 4, 5};
long arrayCount = Arrays.stream(array)
    .count();
// 结果: 5

7、max:获取流中最大值的元素

终端操作,用于获取流中最大值的元素。

方法签名

java
Optional<T> max(Comparator<? super T> comparator)

返回值

Optional<T>:包含最大元素的 Optional,如果流为空则返回空的 Optional

示例代码

java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);

// 获取最大值
Optional<Integer> maxNumber = numbers.stream()
    .max(Integer::compareTo);
// 结果: Optional[9]

// 处理Optional结果
maxNumber.ifPresent(max -> System.out.println("最大值是: " + max));
// 输出: 最大值是: 9

List<Integer> emptyList = Collections.emptyList();
Optional<Integer> emptyMax = emptyList.stream()
    .max(Integer::compareTo);
// 结果: Optional.empty

8、min:获取流中最小值的元素

终端操作,用于获取流中最小值的元素。

方法签名

java
Optional<T> min(Comparator<? super T> comparator)

返回值

Optional<T>:包含最小元素的 Optional,如果流为空则返回空的 Optional

示例代码

java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);

// 获取最小值
Optional<Integer> minNumber = numbers.stream()
    .min(Integer::compareTo);
// 结果: Optional[1]

// 处理Optional结果
minNumber.ifPresent(min -> System.out.println("最小值是: " + min));
// 输出: 最小值是: 1

List<Integer> emptyList = Collections.emptyList();
Optional<Integer> emptyMin = emptyList.stream()
    .min(Integer::compareTo);
// 结果: Optional.empty

规约是什么

规约是 Stream API 的核心概念之一,指将流中的元素组合起来,得到一个单一的结果值。

什么是规约?

规约(Reduction)也称为归约,是将一个集合通过某种操作"缩减"为单个值的过程。

生活中的类比

  • 求和:1 + 2 + 3 + 4 = 10(4个数规约为1个数)
  • 找最大值:[3, 1, 4, 2] → 4(多个数规约为最大值)
  • 连接字符串:["a", "b", "c"] → "abc"

常用方法

方法签名

java
// 1. 一个参数:累积函数
Optional<T> reduce(BinaryOperator<T> accumulator)

// 2. 两个参数:初始值 + 累积函数
T reduce(T identity, BinaryOperator<T> accumulator)

// 3. 三个参数:初始值 + 累积函数 + 组合函数(并行流用)
<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner)

案例

java
// 示例1:求和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 方式1:使用一个参数的reduce
Optional<Integer> sum1 = numbers.stream()
    .reduce((a, b) -> a + b);
// 结果: Optional[15]

// 方式2:使用两个参数的reduce(推荐)
Integer sum2 = numbers.stream()
    .reduce(0, (a, b) -> a + b);
// 结果: 15

// 方式3:使用方法引用
Integer sum3 = numbers.stream()
    .reduce(0, Integer::sum);
// 结果: 15

规约的三要素

1. 恒等值(Identity)

  • 规约操作的起点值
  • 对于加法:0
  • 对于乘法:1
  • 对于连接字符串:""(空字符串)
  • 必须满足:identity op x = x

2. 累积函数(Accumulator)

  • 定义如何将元素合并到结果中
  • 接收两个参数:当前结果 + 新元素
  • 返回新的结果

3. 组合函数(Combiner)

  • 用于并行流,合并不同线程的结果
  • 顺序流中不会调用