Stream 流式编程中间操作、终端操作

目录

Stream 的中间操作

过滤操作(filter)

映射操作(map)

排序操作(sorted)

截断操作(limit 和 skip)

Stream 的终端操作

forEach 和 peek

聚合操作(reduce 和 collect)

匹配操作(allMatch、anyMatch 和 noneMatch)

查找操作(findFirst 和 findAny)

统计操作(count、max 和 min)


Stream 的中间操作

过滤操作(filter)

        过滤操作(filter)是 Stream API 中的一种常用操作方法,它接受一个 Predicate 函数作为参数,用于过滤 Stream 中的元素。只有满足 Predicate 条件的元素会被保留下来,而不满足条件的元素将被过滤掉。

过滤操作的语法如下:

Stream filter(Predicate predicate)

其中,T 表示 Stream 元素的类型,predicate 是一个函数式接口 Predicate 的实例,它的泛型参数和 Stream 元素类型一致。

使用过滤操作可以根据自定义的条件来筛选出符合要求的元素,从而对 Stream 进行精确的数据过滤。

下面是一个示例,演示如何使用过滤操作筛选出一个整数流中的偶数:

Stream stream = Stream.of(1, 2, 3, 4, 5); 
Stream filteredStream = stream.filter(n -> n % 2 == 0);
filteredStream.forEach(System.out::println); // 输出结果: 2 4

        在这个示例中,首先创建了一个包含整数的 Stream,并调用 filter() 方法传入一个 Lambda 表达式 n -> n % 2 == 0,表示要筛选出偶数。然后通过 forEach() 方法遍历输出结果。

        需要注意的是,过滤操作返回的是一个新的 Stream 实例,原始的 Stream 不会受到改变。这也是 Stream 操作方法的一个重要特点,它们通常返回一个新的 Stream 实例,以便进行链式调用和组合多个操作步骤。

        在实际应用中,过滤操作可以与其他操作方法结合使用,如映射(map)、排序(sorted)、归约(reduce)等,以实现更复杂的数据处理和转换。而过滤操作本身的优点在于,可以高效地对大型数据流进行筛选,从而提高程序的性能和效率。

映射操作(map)

        映射操作(map)是 Stream API 中的一种常用操作方法,它接受一个 Function 函数作为参数,用于对 Stream 中的每个元素进行映射转换,生成一个新的 Stream。

映射操作的语法如下:

Stream map(Function mapper)

        其中,T 表示原始 Stream 的元素类型,R 表示映射后的 Stream 的元素类型,mapper 是一个函数式接口 Function 的实例,它的泛型参数分别是原始 Stream 元素的类型和映射后的元素类型。

        使用映射操作可以对 Stream 中的元素逐个进行处理或转换,从而获得一个新的 Stream。这个过程通常涉及对每个元素应用传入的函数,根据函数的返回值来构建新的元素。

                下面是一个示例,演示如何使用映射操作将一个字符串流中的每个字符串转换为其长度:

Stream stream = Stream.of("apple", "banana", "cherry"); 
Stream mappedStream = stream.map(s -> s.length()); 
mappedStream.forEach(System.out::println); // 输出结果: 5 6 6

        在这个示例中,首先创建了一个包含字符串的 Stream,并调用 map() 方法传入一个 Lambda 表达式 s -> s.length(),表示要将每个字符串转换为其长度。然后通过 forEach() 方法遍历输出结果。

        需要注意的是,映射操作返回的是一个新的 Stream 实例,原始的 Stream 不会受到改变。这也是 Stream 操作方法的一个重要特点,它们通常返回一个新的 Stream 实例,以便进行链式调用和组合多个操作步骤。

        在实际应用中,映射操作可以与其他操作方法结合使用,如过滤(filter)、排序(sorted)、归约(reduce)等,以实现更复杂的数据处理和转换。而映射操作本身的优点在于,可以通过简单的函数变换实现对原始数据的转换,减少了繁琐的循环操作,提高了代码的可读性和维护性。

        需要注意的是,映射操作可能引发空指针异常(NullPointerException),因此在执行映射操作时,应确保原始 Stream 中不包含空值,并根据具体情况进行空值处理。

排序操作(sorted)

        排序操作(sorted)是 Stream API 中的一种常用操作方法,它用于对 Stream 中的元素进行排序。排序操作可以按照自然顺序或者使用自定义的比较器进行排序。

        排序操作的语法如下:

Stream sorted() Stream sorted(Comparator comparator)

        第一种语法形式中,sorted() 方法会根据元素的自然顺序进行排序。如果元素实现了 Comparable 接口并且具备自然顺序,那么可以直接调用该方法进行排序。

        第二种语法形式中,sorted(Comparator comparator) 方法接受一个比较器(Comparator)作为参数,用于指定元素的排序规则。通过自定义比较器,可以对非 Comparable 类型的对象进行排序。

        下面是一个示例,演示如何使用排序操作对一个字符串流进行排序:

Stream stream = Stream.of("banana", "apple", "cherry"); 
Stream sortedStream = stream.sorted(); 
sortedStream.forEach(System.out::println); // 输出结果: apple banana cherry

        在这个示例中,首先创建了一个包含字符串的 Stream,并直接调用 sorted() 方法进行排序。然后通过 forEach() 方法遍历输出结果。

        需要注意的是,排序操作返回的是一个新的 Stream 实例,原始的 Stream 不会受到改变。这也是 Stream 操作方法的一个重要特点,它们通常返回一个新的 Stream 实例,以便进行链式调用和组合多个操作步骤。

        在实际应用中,排序操作可以与其他操作方法结合使用,如过滤(filter)、映射(map)、归约(reduce)等,以实现更复杂的数据处理和转换。排序操作本身的优点在于,可以将数据按照特定的顺序排列,便于查找、比较和分析。

        需要注意的是,排序操作可能会影响程序的性能,特别是对于大型数据流或者复杂的排序规则。因此,在实际应用中,需要根据具体情况进行权衡和优化,选择合适的算法和数据结构来提高排序的效率。

截断操作(limit 和 skip)

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

  1. limit(n):保留流中的前n个元素,返回一个包含最多n个元素的新流。如果流中元素少于n个,则返回原始流。
  2. skip(n):跳过流中的前n个元素,返回一个包含剩余元素的新流。如果流中元素少于n个,则返回一个空流。

下面分别详细介绍这两个方法的使用。

limit(n) 方法示例:

Stream stream = Stream.of(1, 2, 3, 4, 5);
Stream limitedStream = stream.limit(3);
limitedStream.forEach(System.out::println); // 输出结果: 1 2 3

        在这个示例中,我们创建了一个包含整数的 Stream,并调用 limit(3) 方法来保留前三个元素。然后使用 forEach() 方法遍历输出结果。

skip(n) 方法示例:

Stream stream = Stream.of(1, 2, 3, 4, 5); 
Stream skippedStream = stream.skip(2);
skippedStream.forEach(System.out::println); // 输出结果: 3 4 5

        在这个示例中,创建了一个包含整数的 Stream,并调用 skip(2) 方法来跳过前两个元素。然后使用 forEach() 方法遍历输出结果。

        需要注意的是,截断操作返回的是一个新的 Stream 实例,原始的 Stream 不会受到改变。这也是 Stream 操作方法的一个重要特点,它们通常返回一个新的 Stream 实例,以便进行链式调用和组合多个操作步骤。

        截断操作在处理大型数据流或需要对数据进行切分和分页显示的场景中非常有用。通过限制或跳过指定数量的元素,可以控制数据的大小和范围,提高程序的性能并减少不必要的计算。

        需要注意的是,在使用截断操作时需要注意流的有界性。如果流是无界的(例如 Stream.generate()),那么使用 limit() 方法可能导致程序陷入无限循环,而使用 skip() 方法则没有意义。

Stream 的终端操作

forEach 和 peek

        forEach和peek都是Stream API中用于遍历流中元素的操作方法,它们在处理流的过程中提供了不同的功能和使用场景。

  1. forEach: forEach是一个终端操作方法,它接受一个Consumer函数作为参数,对流中的每个元素执行该函数。它没有返回值,因此无法将操作结果传递给后续操作。forEach会遍历整个流,对每个元素执行相同的操作。
List names = Arrays.asList("Alice", "Bob", "Charlie"); 
names.stream() .forEach(System.out::println);

        这个示例中,我们创建了一个包含字符串的List,并通过stream()方法将其转换为流。然后使用forEach方法遍历输出每个元素的值。

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

示例代码:

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

        在这个示例中,首先将List转换为流,并通过map方法将每个元素转换为大写字母。然后使用peek方法在转换之前输出每个元素的值。最后通过collect方法将元素收集到一个新的List中。

        需要注意的是,无论是forEach还是peek,它们都是用于在流的处理过程中执行操作。区别在于forEach是终端操作,不返回任何结果,而peek是中间操作,可以和其他操作方法进行组合和链式调用。

        根据使用场景和需求,选择使用forEach或peek来遍历流中的元素。如果只是需要遍历输出元素,不需要操作结果,则使用forEach。如果需要在遍历过程中执行一些其他操作,并将元素传递给后续操作,则使用peek。

聚合操作(reduce 和 collect)

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

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

示例代码:

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

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

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

示例代码:

List 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 是用于进行匹配操作的方法,它们可以用来检查流中的元素是否满足特定的条件。

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

示例代码:

List 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。

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

示例代码:

List 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。

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

示例代码:

List 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 是用于查找操作的方法,它们可以用来从流中获取满足特定条件的元素。

  1. findFirst: findFirst 方法用于返回流中的第一个元素。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的第一个元素的 Optional。

示例代码:

List names = Arrays.asList("Alice", "Bob", "Charlie"); 
Optional first = names.stream() .findFirst();
first.ifPresent(System.out::println); // 输出结果: Alice

        在这个示例中,创建了一个包含字符串的 List,并通过 stream() 方法将其转换为流。然后使用 findFirst 方法获取流中的第一个元素,并使用 ifPresent 方法判断 Optional 是否包含值,并进行相应的处理。

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

示例代码:

List numbers = Arrays.asList(1, 2, 3, 4, 5); 
Optional 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 相当。

统计操作(count、max 和 min)

        在 Stream API 中,count、max 和 min 是用于统计操作的方法,它们可以用来获取流中元素的数量、最大值和最小值。

  1. count: count 方法用于返回流中元素的数量。它返回一个 long 类型的值,表示流中的元素个数。

示例代码:

List numbers = Arrays.asList(1, 2, 3, 4, 5); 
long count = numbers.stream() .count(); 
System.out.println(count); // 输出结果: 5

        在这个示例中,创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 count 方法获取流中元素的数量,并将结果输出。

  1. max: max 方法用于返回流中的最大值。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的最大值的 Optional。

示例代码:

List numbers = Arrays.asList(1, 2, 3, 4, 5); 
Optional max = numbers.stream() .max(Integer::compareTo); 
max.ifPresent(System.out::println); // 输出结果: 5

        在这个示例中,创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 max 方法获取流中的最大值,并使用 ifPresent 方法判断 Optional 是否包含值,并进行相应的处理。

  1. min: min 方法用于返回流中的最小值。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的最小值的 Optional。

示例代码:

List numbers = Arrays.asList(1, 2, 3, 4, 5); 
Optional min = numbers.stream() .min(Integer::compareTo); 
min.ifPresent(System.out::println); // 输出结果: 1

        在这个示例中,创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 min 方法获取流中的最小值,并使用 ifPresent 方法判断 Optional 是否包含值,并进行相应的处理。

这些统计操作方法提供了一种便捷的方式来对流中的元素进行数量、最大值和最小值的计算。通过返回 Optional 对象,可以避免空指针异常。

更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)

你可能感兴趣的:(java,前端,javascript)