Java的Stream操作详解

Posted 征途黯然.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的Stream操作详解相关的知识,希望对你有一定的参考价值。

[1] 总览

  Java Stream是一种基于流式处理的API,它提供了一种简洁而高效的方式来处理集合、数组或任何其他数据源中的元素。在使用Java Stream时,可以通过链式调用一系列的中间操作终端操作来对数据源中的元素进行处理和转换,而不需要显式地使用循环或条件语句。

  Java Stream主要分为两种类型:中间操作和终端操作。中间操作是在数据源上进行的转换操作,每次操作都会返回一个新的Stream实例,以便继续进行操作。而终端操作是指对Stream进行最终操作的操作,如收集、计算或迭代等。

1)中间操作,可以有多个,每次返回一个新的流,可进行链式操作。
2)终端操作,只能有一个,每次执行完,这个流就结束了,因此只能放在最后。

  一些常见的Java Stream操作:

  · 过滤操作(filter):使用给定的谓词过滤数据源中的元素。

  · 映射操作(map):将数据源中的每个元素映射为新元素。

  · 排序操作(sorted):按指定的顺序对数据源中的元素进行排序。

  · 去重操作(distinct):从数据源中删除重复的元素。

  · 统计操作(count、min、max、sum、average):统计数据源中的元素。

  · 收集操作(collect):将数据源中的元素收集到一个集合中。

  · 迭代操作(forEach):对数据源中的每个元素进行迭代操作。

  · 匹配操作(allMatch、anyMatch、noneMatch):使用给定的谓词测试数据源中的元素是否匹配。

  · 并行流操作(parallelStream):使用并行处理加速处理数据源中的元素。

  Java Stream可以使程序员更加简洁、高效地处理数据,使我们的代码更加易于阅读和维护。它还提供了许多其他功能,如支持延迟计算、内部迭代等。在实际的开发中,Java Stream可以广泛应用于集合处理、数据筛选和转换等场景。

[2] 代码演示

[2.1] 过滤操作(filter) 中间操作

  这个例子中,首先创建了一个字符串列表words。接着,使用stream()方法将列表转换为一个Stream对象。然后,使用filter()方法来过滤出长度大于5的元素,得到了新的Stream对象。最后,使用collect()方法将过滤后的结果收集到一个List中,并输出结果[banana, orange]

List<String> words = Arrays.asList("apple", "banana", "orange", "pear", "grape");
List<String> longWords = words.stream()
                              .filter(word -> word.length() > 5)
                              .collect(Collectors.toList());
System.out.println(longWords);
// [banana, orange]

这里使用了collect把stream流转化为List。

[2.2] 映射操作(map) 中间操作

  在这个例子中,首先创建了一个整数列表numbers。然后,使用stream()方法将列表转换为一个Stream对象。接着,使用map()方法将每个元素映射为它的平方,并得到新的Stream对象。最后,我们使用collect()方法将映射后的结果收集到一个List中,并输出结果[1, 4, 9, 16, 25]

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream()
                                .map(n -> n * n)
                                .collect(Collectors.toList());
System.out.println(squares);
// [1, 4, 9, 16, 25]

[2.3] 排序操作(sorted) 中间操作

  这个例子中,首先创建了一个字符串列表words。然后,使用stream()方法将列表转换为一个Stream对象。接着,使用sorted()方法将元素按字典序排序,并得到新的Stream对象。最后,使用collect()方法将排序后的结果收集到一个List中,并输出结果[apple, banana, grape, orange, pear]

List<String> words = Arrays.asList("apple", "banana", "orange", "pear", "grape");
List<String> sortedWords = words.stream()
                                .sorted()
                                .collect(Collectors.toList());
System.out.println(sortedWords);
// [apple, banana, grape, orange, pear]

[2.4] 去重操作(distinct) 中间操作

  在例子中,使用distinct()方法去除了列表中的重复元素。

List<String> names = Arrays.asList("John", "Jane", "John", "Mike", "Mike");
List<String> distinctNames = names.stream().distinct().collect(Collectors.toList());
System.out.println(distinctNames);
 // 输出:[John, Jane, Mike]

[2.5] 统计操作(count、min、max、sum、average) 终端操作

  在例子中,使用了count()方法计算列表中元素的数量,max()方法获取最大值,min()方法获取最小值,sum()方法求和,average()方法求平均值。

List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
long count = numbers.stream().count();
int max = numbers.stream().max(Integer::compareTo).orElse(0);
int min = numbers.stream().min(Integer::compareTo).orElse(0);
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
double average = numbers.stream().mapToDouble(Integer::intValue).average().orElse(0.0);
System.out.println("Count: " + count); // 输出:Count: 5
System.out.println("Max: " + max); // 输出:Max: 10
System.out.println("Min: " + min); // 输出:Min: 2
System.out.println("Sum: " + sum); // 输出:Sum: 30
System.out.println("Average: " + average); // 输出:Average: 6.0

  stream().max(Integer::compareTo)是使用Java Stream对整数流进行求最大值操作的一种方式。stream()表示将集合或数组转换成一个流;而max()是一个终止操作,会返回流中的最大值。
  在这个例子中,我们使用了方法引用Integer::compareTo,它表示使用Integer类的compareTo方法来进行元素的比较。compareTo方法是Comparable接口中定义的方法,用于比较两个整数的大小,返回一个整数值,如果第一个整数小于第二个整数,返回负数;如果两个整数相等,返回0;如果第一个整数大于第二个整数,返回正数。
  因此,stream().max(Integer::compareTo)会返回整数流中的最大值,如果流为空,则返回Optional.empty()。如果想获取最小值,可以使用min()方法,使用方式类似。

  使用stream流求和的方法通常是先将流中的元素映射为整数类型,然后使用sum()方法对整数流进行求和。在这里,mapToInt(Integer::intValue)方法用于将流中的元素转换为整型,并返回一个新的IntStream类型的流。因为IntStream是一个基本类型的流,因此它具有更高的效率和更低的内存消耗。

[2.6] 收集操作(collect) 终端操作

  在例子中,使用了collect()方法将列表中的元素连接成一个字符串,使用joining()方法指定连接符。

List<String> names = Arrays.asList("John", "Jane", "Mike");
String joinedNames = names.stream().collect(Collectors.joining(", "));
System.out.println(joinedNames); // 输出:John, Jane, Mike

  Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors常用于返回列表或字符串:

.collect(Collectors.toList()) // 返回列表
.collect(Collectors.joining(", ") // 返回字符串

[2.7] 迭代操作(forEach) 终端操作

  在例子中,使用了forEach()方法对列表中的每个元素执行一些操作,这里是打印一个问候语。

List<String> names = Arrays.asList("John", "Jane", "Mike");
names.stream().forEach(name -> System.out.println("Hello, " + name));

[2.8] 匹配操作(allMatch、anyMatch、noneMatch) 终端操作

  allMatch:判断数据源中所有的元素是否都满足给定的条件,如果全部满足则返回true,否则返回false

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allMatch = numbers.stream().allMatch(n -> n > 0); // true

  anyMatch:判断数据源中是否有任何一个元素满足给定的条件,如果有则返回true,否则返回false

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyMatch = numbers.stream().anyMatch(n -> n > 4); // true

  noneMatch:判断数据源中是否没有任何一个元素满足给定的条件,如果没有则返回true,否则返回false

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noneMatch = numbers.stream().noneMatch(n -> n < 0); // true

[2.9] 并行流操作(parallelStream)

  并行流操作是Java Stream提供的一种并行处理数据源中元素的方式。与串行流不同,当使用并行流时,Stream会将数据源分成多个部分,并在多个线程上同时处理这些部分,以加速数据处理的速度。

  在例子中,首先将一个包含5个整数的列表转换为一个Stream对象,然后调用parallelStream()方法将该Stream对象转换为并行流对象。接着,使用filter操作筛选出列表中的偶数,最后使用count操作计算符合条件的元素的数量。

  使用并行流操作可以加速数据处理的速度,但也需要注意并发安全和线程安全等问题,确保在使用并行流时能够正确处理数据。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.parallelStream().filter(n -> n % 2 == 0).count(); // 2

[3] 关于mapToxxx

  Java Stream API中的mapToxxx()方法可以将Stream中的每个元素映射为一个新的值,然后返回一个新的Stream。其中xxx代表了一个基本类型,如IntLongDouble

  以下是mapToxxx()方法的一些常见用法:

  1. mapToInt(): 将Stream中的每个元素映射为一个int值,并返回一个新的IntStream

  例如,将一个字符串列表中的所有字符串的长度映射为一个int值的示例代码如下:

List<String> strings = Arrays.asList("Java", "Stream", "API");
IntStream lengths = strings.stream().mapToInt(String::length);

  2. mapToLong(): 将Stream中的每个元素映射为一个long值,并返回一个新的LongStream

  例如,将一个数字列表中的每个数字乘以2,然后将结果映射为一个long值的示例代码如下:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
LongStream doubledNumbers = numbers.stream().mapToLong(n -> n * 2L);

  3. mapToDouble(): 将Stream中的每个元素映射为一个double值,并返回一个新的DoubleStream

  例如,将一个数字列表中的每个数字除以2,然后将结果映射为一个double值的示例代码如下:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
DoubleStream halfNumbers = numbers.stream().mapToDouble(n -> n / 2.0);

  需要注意的是,由于基本类型无法为null,因此如果在映射过程中存在可能返回null的情况,需要先使用filter()方法过滤掉这些元素,以避免空指针异常的出现。

以上是关于Java的Stream操作详解的主要内容,如果未能解决你的问题,请参考以下文章

java8新特性Stream流操作详解及实战3

Java8中聚合操作collectreduce方法详解

Java8中聚合操作collectreduce方法详解

Java 8 Stream 流详解

Java8 Stream用法详解

Java8中聚合操作collectreduce方法详解