一、简介
1、什么是集合流式编程
集合流式编程(Stream API)是Java 8引入的一个功能强大的特性,它提供了一种更简洁、更高效的方式来操作集合数据。它的设计目标是让开发者能够以一种更声明式的风格来处理集合数据,减少了显式的迭代和条件判断,使代码更加清晰和易于理解。
集合流式编程的主要特点如下:
链式操作:集合流提供了一系列的方法,这些方法可以通过链式调用来进行操作。这种链式操作的方式使得代码更加简洁、易读,可以通过不同的方法组合出复杂的操作流程。
内部迭代:传统的集合操作需要显式地使用迭代器或循环来遍历集合元素,而集合流式编程使用内部迭代的方式来处理数据,这意味着我们只需要关注对数据的操作,而不需要关注具体的迭代过程。
惰性求值:集合流式编程中的操作分为中间操作和终端操作。中间操作是对流进行处理的过程,它们不会立即执行,而是在终端操作被调用时才会触发执行。这种惰性求值的机制可以优化性能,避免不必要的计算。
并行处理:集合流式编程提供了并行处理的能力,可以利用多核处理器的优势,对数据进行并行操作,提高处理速度。通过调用parallel()
方法,可以将串行流转换为并行流,实现并行处理。
集合流式编程的使用可以极大地简化集合数据的处理代码,提高开发效率。以下是一个简单的示例,展示了集合流的使用:
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 筛选偶数
.mapToInt(n -> n) // 转换为int类型
.sum(); // 求和
System.out.println(sum); // 输出:30
在上面的示例中,我们使用stream()
方法将List转换为流,然后通过filter()
方法筛选出偶数,再通过mapToInt()
方法将流中的元素转换为int类型,最后使用sum()
方法求和。整个过程通过链式调用完成,代码简洁明了。
除了上述示例中的操作方法,集合流还提供了丰富的操作,如映射(map()
)、排序(sorted()
)、去重(distinct()
)、分组(groupBy()
)等,可以根据具体需求进行选择和组合。
需要注意的是,
集合流是一次性使用的,一旦流被消费或终端操作执行完毕,就不能再次使用。如果需要对同一集合进行多次操作,可以使用Stream
的Supplier
来创建新的流。
总之,集合流式编程为Java开发者提供了一种更简洁、更高效的集合数据处理方式,它使得代码更具可读性,同时还提供了并行处理的能力,可以提高性能。
2、为什么要使用集合流式编程
使用集合流式编程有以下几个优点:
简洁和可读性更好:集合流式编程使用链式操作的方式,可以将多个操作组合在一起,形成一个流畅的操作链。这种链式调用的方式使得代码更加简洁、易读,可以更清晰地表达对集合数据的处理逻辑,减少了显式的迭代和条件判断。
代码可维护性更高:集合流式编程能够将数据处理的步骤和逻辑清晰地表达出来,使代码更易于维护和理解。每个操作都是独立的,可以按需添加、删除或修改操作,而不需要关心具体的迭代过程。这种模块化的设计使得代码更加灵活,易于扩展和修改。
提高开发效率:使用集合流式编程可以减少代码量,并且能够通过链式操作一次性完成多个处理步骤,避免了中间变量的定义和赋值。这种简洁的编码风格可以提高开发效率,减少冗余代码的编写,从而更快地实现业务需求。
可以实现更高效的并行处理:集合流式编程提供了并行处理的能力,可以利用多核处理器的优势,将数据分成多个部分并行处理,从而提高处理速度。通过调用parallel()
方法,可以将串行流转换为并行流,实现并行处理。这在处理大规模数据集合时尤为重要,可以充分利用硬件资源,加快数据处理速度。
与函数式编程相结合:集合流式编程借鉴了函数式编程的思想,强调对数据的转换和操作,而不是对迭代过程的关注。它提供了丰富的函数式操作方法,如map()
、filter()
、reduce()
等,使得代码更加抽象和灵活。函数式编程的特性,如不可变性、纯函数等,也有助于编写更可靠、可测试和可维护的代码。
3、集合流式编程的步骤
集合流式编程的步骤可以概括为以下几个:
创建流:首先,需要将要操作的集合转换为流。可以使用集合的stream()
方法来创建一个顺序流(Sequential Stream)或parallelStream()
方法来创建一个并行流(Parallel Stream)。顺序流适用于单线程操作,而并行流适用于多线程并行操作。
中间操作:对创建的流进行中间操作。中间操作是一系列对流进行处理的操作,可以通过链式调用来组合多个中间操作。中间操作并不会立即执行,而是在终端操作被调用时才会触发执行。常见的中间操作包括筛选(filter()
)、映射(map()
)、去重(distinct()
)、排序(sorted()
)等。
终端操作:对经过中间操作的流进行终端操作。终端操作是流操作的最后一步,会触发流的执行并产生结果。终端操作可以是获取结果(如collect()
、toArray()
)、聚合计算(如count()
、sum()
、max()
、min()
)、遍历打印(如forEach()
)等。
需要注意的是,集合流式编程的中间操作和终端操作可以根据需求进行选择和组合。中间操作可以有多个,但终端操作只能有一个,并且终端操作是触发流执行的标志。
以下是一个示例,演示了集合流式编程的基本步骤:
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 中间操作:筛选偶数
.mapToInt(n -> n) // 中间操作:转换为int类型
.sum(); // 终端操作:求和
System.out.println(sum); // 输出:30
在上面的示例中,我们首先使用stream()
方法将numbers
集合转换为顺序流,然后通过filter()
方法筛选出偶数,再通过mapToInt()
方法将流中的元素转换为int类型,最后使用sum()
方法求和。整个过程通过链式调用完成,代码简洁明了。
需要根据具体的需求选择合适的中间操作和终端操作,以实现预期的数据处理和结果输出。集合流式编程的灵活性和简洁性可以提高开发效率和代码可读性。
二、最终操作
将流中的数据整合到一起,可以存入一个集合,也可以直接对流中的数据进行遍历、数据统计.….,通过最终操作,需要掌握如何从流中提取出来我们想要的信息。注意事项:最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会销毁。如果使用一个已经关闭了的流,会出现异常。
1、最终操作collect
collect
是一个常用的终端操作,它将流中的元素收集到一个集合或其他数据结构中。collect
操作接受一个Collector
参数,该参数定义了如何收集元素和生成最终的结果。
下面是一个使用collect
操作的案例,演示了如何将字符串列表中的元素连接成一个字符串:
List strings = Arrays.asList("Hello", "world", "!");
String result = strings.stream()
.collect(Collectors.joining(" "));
System.out.println(result); // 输出:Hello world !
在上面的例子中,我们使用collect
操作将字符串列表中的元素连接成一个字符串。我们通过Collectors.joining(" ")
指定了连接字符串的分隔符为空格。最终得到了"Hello world !"的结果。
除了字符串的连接,collect
操作还可以将元素收集到列表、集合、映射等数据结构中。下面是一个将整数流中的偶数收集到一个新的列表的示例:
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出:[2, 4]
在上面的例子中,我们使用collect
操作将整数流中的偶数收集到一个新的列表中。通过Collectors.toList()
指定了将元素收集到列表中。最终得到了包含偶数[2, 4]的结果列表。
除了toList()
,Java还提供了许多其他的Collector
工具类,如toSet()
(将元素收集到集合)、toMap()
(将元素收集到映射)、toCollection()
(将元素收集到自定义集合)、summarizingInt()
(统计信息汇总)等,可以根据需求选择合适的Collector
来实现元素的收集。
collect
操作在集合流式编程中非常有用,它能够将流中的元素收集到不同的数据结构中,方便进行后续的处理和操作。通过使用不同的Collector
,我们可以实现各种灵活的元素收集需求。
2、最终操作reduce
reduce
是一个常用的终端操作,它通过对流中的元素进行逐个操作,并将操作的结果累积到一个最终的值上。它接受一个二元操作符作为参数,该操作符定义了如何将两个元素进行操作并生成一个新的值。
下面是一个使用reduce
操作的案例,演示了如何计算集合中所有元素的和:
List numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum); // 输出:15
在上面的例子中,我们使用reduce
操作计算了集合中所有元素的和。初始值为0,二元操作符(a, b) -> a + b
将前一个结果与当前元素相加得到新的结果,最终得到了总和15。
reduce
操作也可以与并行流一起使用,实现并行计算。下面是一个使用并行流和reduce
操作计算集合中所有元素的乘积的示例:
List numbers = Arrays.asList(1, 2, 3, 4, 5);
int product = numbers.parallelStream()
.reduce(1, (a, b) -> a * b);
System.out.println(product); // 输出:120
在上面的例子中,我们使用了并行流parallelStream()
,通过reduce
操作计算了集合中所有元素的乘积。初始值为1,二元操作符(a, b) -> a * b
将前一个结果与当前元素相乘得到新的结果,最终得到了乘积120。
需要注意的是,reduce
操作的初始值是可选的,如果流为空,那么初始值就是结果。此外,如果初始值为null
,则需要使用Optional
来处理结果。
reduce
操作在集合流式编程中非常有用,可以通过定义不同的二元操作符,实现对流中元素的累积操作,从而得到最终的结果。它的灵活性使得我们可以进行各种复杂的数据处理和计算。
3、最终操作count&forEach
count
和forEach
是常用的最终操作,用于对流中的元素进行计数和遍历操作。
count()
:count
方法用于统计流中的元素数量,并返回统计结果。
下面是一个使用count
操作的案例,统计列表中大于等于 5 的元素个数:
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
long count = numbers.stream()
.filter(n -> n >= 5)
.count();
System.out.println(count); // 输出:6
在上述示例中,我们使用count
方法统计了列表中大于等于 5 的元素的个数。通过链式调用filter
方法筛选满足条件的元素,然后调用count
方法获取统计结果。
forEach(Consumer