在Java中,Stream API是Java 8引入的一个核心特性,它提供了一种高效且表达力强的方式来处理数据集合(如List、Set)。Stream API可以让我们以声明式的方式处理数据集合,通过一系列的中间操作和终端操作来完成复杂的数据处理任务,同时保持代码的简洁性和可读性。本章将深入解析Java Stream API中常用的操作方法,帮助读者掌握函数式编程在Java中的实践应用。
在深入讨论Stream操作方法之前,首先了解如何创建Stream是必要的。Stream可以通过多种方式创建,包括但不限于:
stream()
和parallelStream()
方法:任何实现了Collection
接口的集合都可以调用stream()
方法来获取顺序流,或parallelStream()
方法来获取并行流。stream()
方法:对于数组,可以使用Arrays.stream(T... array)
静态方法创建流。Stream.of(T... values)
,可以直接从一组值中创建流;Stream.empty()
创建一个空的流;Stream.builder()
则提供了一个构建器模式来创建流。IntStream
、LongStream
、DoubleStream
,这些流提供了对基本数据类型的优化处理。中间操作会返回流本身,支持链式调用。它们可以对流中的元素进行各种处理,但处理结果不会立即产生,而是等待终端操作触发时才执行。
filter(Predicate<? super T> predicate)
:过滤流中的元素,只保留符合给定条件的元素。
List<String> filtered = list.stream()
.filter(s -> s.startsWith("J"))
.collect(Collectors.toList());
map(Function<? super T, ? extends R> mapper)
:将流中的每个元素映射成另一种形式。
List<String> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
sorted()
和 sorted(Comparator<? super T> comparator)
:对流中的元素进行排序。无参sorted()
方法要求流中的元素实现了Comparable
接口;有参版本允许自定义排序规则。
list.stream()
.sorted()
.forEach(System.out::println);
list.stream()
.sorted(Comparator.comparingInt(String::length).reversed())
.forEach(System.out::println);
limit(long maxSize)
:截断流,使其包含的元素不超过给定数量。
skip(long n)
:跳过流中的前n个元素。
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
:将流中的每个元素都转换成流,然后将所有流连接成一个流。常用于处理嵌套集合。
distinct()
:去除流中的重复元素(根据元素的equals()
和hashCode()
方法)。
终端操作会触发流的计算,并产生结果。一旦执行了终端操作,流就不能再被使用。
forEach(Consumer<? super T> action)
:对流中的每个元素执行给定的操作。
collect(Collectors.toList())
等收集器:将流中的元素累积成一个集合(如List、Set、Map等)。Collectors
类提供了多种收集器实现。
List<String> collected = list.stream()
.filter(s -> s.startsWith("J"))
.collect(Collectors.toList());
reduce(BinaryOperator<T> accumulator)
和 reduce(T identity, BinaryOperator<T> accumulator)
:通过二元操作函数对流中的元素进行归约操作,如求和、求最大值等。
Optional<String> concatenated = list.stream()
.reduce((s1, s2) -> s1 + "," + s2);
int sum = list.stream()
.mapToInt(String::length)
.reduce(0, Integer::sum);
findFirst()
和 findAny()
:返回流中的第一个或任意一个元素(对于并行流,findAny()
可能更快)。
anyMatch(Predicate<? super T> predicate)
、allMatch(Predicate<? super T> predicate)
、noneMatch(Predicate<? super T> predicate)
:检查流中的元素是否至少有一个、全部或没有符合给定条件的元素。
min(Comparator<? super T> comparator)
和 max(Comparator<? super T> comparator)
:根据提供的比较器,返回流中的最小或最大元素。
count()
:返回流中的元素数量。
toArray()
:将流中的元素收集到一个新的数组中。
在介绍Stream操作方法时,不得不提的是并行流。通过调用集合的parallelStream()
方法,可以获得一个并行流,它允许Java运行时自动将流操作分解为多个子任务,并在多个线程上并行执行。然而,并行流并不总是带来性能提升,其效率取决于多个因素,包括数据的大小、处理逻辑的复杂度以及底层硬件的性能。
为了加深理解,我们通过一个实战案例来演示Stream API的使用。假设我们有一个学生列表,每个学生有姓名、年龄和成绩,现在我们需要找出成绩最高的学生的姓名。
List<Student> students = // 假设这是已经初始化好的学生列表
Optional<Student> topStudent = students.stream()
.max(Comparator.comparingInt(Student::getScore));
if (topStudent.isPresent()) {
System.out.println("成绩最高的学生是:" + topStudent.get().getName());
} else {
System.out.println("学生列表为空");
}
在这个例子中,我们首先通过stream()
方法将学生列表转换为流,然后使用max()
方法和成绩比较器Comparator.comparingInt(Student::getScore)
来找出成绩最高的学生。max()
方法返回的是一个Optional<Student>
对象,它可能包含或不包含值,因此我们需要通过isPresent()
方法来检查是否找到了成绩最高的学生。
通过本章的学习,我们深入了解了Java Stream API中常用的操作方法,包括中间操作和终端操作,以及并行流的概念和使用注意事项。Stream API为Java提供了一种强大且灵活的数据处理机制,使得我们能够以声明式的方式编写出既简洁又高效的代码。希望读者能够通过实践不断加深对Stream API的理解和应用,从而在实际开发中更加得心应手。