Stream 流式编程不常用API讲解

常用的 Stream 操作方法

FlatMap

扁平映射(FlatMap):flatMap() 方法类似于 map() 方法,不同之处在于它可以将每个元素映射为一个流,并将所有流连接成一个流。这主要用于解决嵌套集合的情况。例如:

List<List<Integer>> nestedList = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
Stream<Integer> flattenedStream = nestedList.stream().flatMap(List::stream); // 扁平化为一个流

截断操作(limit 和 skip)

截断操作(limit和skip)是 Stream API 中常用的操作方法,用于在处理流的过程中对元素进行截断。

limit(n):保留流中的前n个元素,返回一个包含最多n个元素的新流。如果流中元素少于n个,则返回原始流。
skip(n):跳过流中的前n个元素,返回一个包含剩余元素的新流。如果流中元素少于n个,则返回一个空流。
需要注意的是,截断操作返回的是一个新的 Stream 实例,原始的 Stream 不会受到改变。这也是 Stream 操作方法的一个重要特点,它们通常返回一个新的 Stream 实例,以便进行链式调用和组合多个操作步骤。
截断操作在处理大型数据流或需要对数据进行切分和分页显示的场景中非常有用。通过限制或跳过指定数量的元素,可以控制数据的大小和范围,提高程序的性能并减少不必要的计算。
需要注意的是,在使用截断操作时需要注意流的有界性。如果流是无界的(例如 Stream.generate()),那么使用 limit() 方法可能导致程序陷入无限循环,而使用 skip() 方法则没有意义。

截断(Limit)

`
Stream stream = Stream.of(1, 2, 3, 4, 5);
Stream limitedStream = stream.limit(3); // 只保留前 3 个元素

`

Skip

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> skippedStream = stream.skip(2); // 跳过前 2 个元素

Distinct

Stream<Integer> stream = Stream.of(1, 2, 2, 3, 3, 3);
Stream<Integer> distinctStream = stream.distinct(); // 去重

Stream 的终端操作

peek

peek: peek是一个中间操作方法,它接受一个Consumer函数作为参数,对流中的每个元素执行该函数。与forEach不同的是,peek方法会返回一个新的流,该流中的元素和原始流中的元素相同。

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> upperCaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .peek(System.out::println)
                                   .collect(Collectors.toList());

需要注意的是,无论是forEach还是peek,它们都是用于在流的处理过程中执行操作。区别在于forEach是终端操作,不返回任何结果,而peek是中间操作,可以和其他操作方法进行组合和链式调用。
根据使用场景和需求,选择使用forEach或peek来遍历流中的元素。如果只是需要遍历输出元素,不需要操作结果,则使用forEach。如果需要在遍历过程中执行一些其他操作,并将元素传递给后续操作,则使用peek。

聚合操作(reduce 和 collect)

reduce和collect都是Stream API中用于聚合操作的方法,它们可以将流中的元素进行汇总、计算和收集。

reduce:

reduce是一个终端操作方法,它接受一个BinaryOperator函数作为参数,对流中的元素逐个进行合并操作,最终得到一个结果。该方法会将流中的第一个元素作为初始值,然后将初始值与下一个元素传递给BinaryOperator函数进行计算,得到的结果再与下一个元素进行计算,以此类推,直到遍历完所有元素。

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream()
                               .reduce((a, b) -> a + b);
sum.ifPresent(System.out::println); // 输出结果: 15

在这个示例中,我们创建了一个包含整数的List,并通过stream()方法将其转换为流。然后使用reduce方法对流中的元素进行求和操作,将每个元素依次相加,得到结果15。

collect:

collect是一个终端操作方法,它接受一个Collector接口的实现作为参数,对流中的元素进行收集和汇总的操作。Collector接口定义了一系列用于聚合操作的方法,例如收集元素到List、Set、Map等容器中,或进行字符串连接、分组、计数等操作。

示例代码:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String joinedNames = names.stream()
                          .collect(Collectors.joining(", "));
System.out.println(joinedNames); // 输出结果: Alice, Bob, Charlie

在这个示例中,我们创建了一个包含字符串的List,并通过stream()方法将其转换为流。然后使用collect方法将流中的元素连接成一个字符串,每个元素之间使用逗号和空格分隔。
需要注意的是,reduce和collect都是终端操作,它们都会触发流的遍历和处理。不同的是,reduce方法用于对流中的元素进行累积计算,得到一个最终结果;而collect方法用于对流中的元素进行收集和汇总,得到一个容器或其他自定义的结果。
在选择使用reduce还是collect时,可以根据具体需求和操作类型来决定。如果需要对流中的元素进行某种计算和合并操作,得到一个结果,则使用reduce。如果需要将流中的元素收集到一个容器中,进行汇总、分组、计数等操作,则使用collect。

匹配操作(allMatch、anyMatch 和 noneMatch)

在 Stream API 中,allMatch、anyMatch 和 noneMatch 是用于进行匹配操作的方法,它们可以用来检查流中的元素是否满足特定的条件。

allMatch:

allMatch 方法用于判断流中的所有元素是否都满足给定的条件。当流中的所有元素都满足条件时,返回 true;如果存在一个元素不满足条件,则返回 false。

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream()
                         .allMatch(n -> n % 2 == 0);
System.out.println(allEven); // 输出结果: false

在这个示例中,我们创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 allMatch 方法判断流中的元素是否都是偶数。由于列表中存在奇数,所以返回 false。

anyMatch:

anyMatch 方法用于判断流中是否存在至少一个元素满足给定的条件。当流中至少有一个元素满足条件时,返回 true;如果没有元素满足条件,则返回 false。

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEven = numbers.stream()
                         .anyMatch(n -> n % 2 == 0);
System.out.println(hasEven); // 输出结果: true

在这个示例中,我们创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 anyMatch 方法判断流中是否存在偶数。由于列表中存在偶数,所以返回 true。

noneMatch:

noneMatch 方法用于判断流中的所有元素是否都不满足给定的条件。当流中没有元素满足条件时,返回 true;如果存在一个元素满足条件,则返回 false。

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noneNegative = numbers.stream()
                             .noneMatch(n -> n < 0);
System.out.println(noneNegative); // 输出结果: true

在这个示例中,我们创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 noneMatch 方法判断流中的元素是否都是非负数。由于列表中的元素都是非负数,所以返回 true。
需要注意的是,allMatch、anyMatch 和 noneMatch 都是终端操作,它们会遍历流中的元素直到满足条件或处理完所有元素。在性能上,allMatch 和 noneMatch 在第一个不匹配的元素处可以立即返回结果,而 anyMatch 在找到第一个匹配的元素时就可以返回结果。

查找操作(findFirst 和 findAny)

在 Stream API 中,findFirst 和 findAny 是用于查找操作的方法,它们可以用来从流中获取满足特定条件的元素。

findAny:

findAny 方法用于返回流中的任意一个元素。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的任意一个元素的 Optional。在顺序流中,通常会返回第一个元素;而在并行流中,由于多线程的处理,可能返回不同的元素。

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> any = numbers.stream()
                               .filter(n -> n % 2 == 0)
                               .findAny();
any.ifPresent(System.out::println); // 输出结果: 2 或 4(取决于并行处理的结果)

在这个示例中,我们创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 filter 方法筛选出偶数,再使用 findAny 方法获取任意一个偶数,最后使用 ifPresent 方法判断 Optional 是否包含值,并进行相应的处理。
需要注意的是,findAny 在并行流中会更有优势,因为在多线程处理时,可以返回最先找到的元素,提高效率。而在顺序流中,findAny 的性能与 findFirst 相当。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值