Java 中的 List.stream:深入探索与实践
简介
在 Java 8 引入 Stream API 之后,处理集合数据的方式发生了重大变革。List.stream
作为 Stream API 的一部分,为开发者提供了一种更简洁、高效且函数式的方式来处理 List
中的元素。通过 List.stream
,我们可以轻松地对列表进行过滤、映射、归约等操作,代码更加易读和维护。本文将深入探讨 List.stream
的基础概念、使用方法、常见实践以及最佳实践,帮助读者全面掌握这一强大的工具。
目录
- 基础概念
- 使用方法
- 创建 Stream
- 中间操作
- 终端操作
- 常见实践
- 过滤元素
- 映射元素
- 查找与匹配
- 归约操作
- 最佳实践
- 性能优化
- 避免副作用
- 正确选择操作
- 小结
- 参考资料
基础概念
Stream 是 Java 8 引入的一个新的抽象概念,它代表了一系列支持顺序和并行聚合操作的元素序列。List.stream
则是从 List
集合创建一个顺序流,List.parallelStream
用于创建并行流。顺序流按照元素的顺序依次处理,而并行流则利用多核处理器并行处理元素,提高处理速度。
Stream 操作分为中间操作和终端操作:
- 中间操作:返回一个新的流,可进行链式调用,如 filter
、map
、sorted
等。
- 终端操作:执行流的处理并返回结果,如 forEach
、collect
、reduce
等。
使用方法
创建 Stream
从 List
创建 Stream 非常简单,只需调用 stream
或 parallelStream
方法:
import java.util.ArrayList;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 创建顺序流
numbers.stream().forEach(System.out::println);
// 创建并行流
numbers.parallelStream().forEach(System.out::println);
}
}
中间操作
Filter
filter
方法用于过滤流中的元素,只保留满足条件的元素:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(java.util.stream.Collectors.toList());
System.out.println(evenNumbers); // 输出: [2, 4]
Map
map
方法用于将流中的每个元素映射为一个新的元素:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(java.util.stream.Collectors.toList());
System.out.println(squaredNumbers); // 输出: [1, 4, 9, 16, 25]
Sorted
sorted
方法用于对流中的元素进行排序:
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(1);
numbers.add(4);
numbers.add(2);
numbers.add(5);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(java.util.stream.Collectors.toList());
System.out.println(sortedNumbers); // 输出: [1, 2, 3, 4, 5]
终端操作
ForEach
forEach
方法用于对流中的每个元素执行一个操作:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.stream()
.forEach(n -> System.out.println(n));
Collect
collect
方法用于将流中的元素收集到一个集合中:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(java.util.stream.Collectors.toList());
System.out.println(result); // 输出: [2, 4]
Reduce
reduce
方法用于将流中的元素归约为一个值:
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum); // 输出: 15
常见实践
过滤元素
在处理数据时,经常需要根据某些条件过滤掉不需要的元素。例如,从用户列表中过滤出年龄大于 18 岁的用户:
import java.util.ArrayList;
import java.util.List;
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
public class FilterExample {
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User("Alice", 16));
users.add(new User("Bob", 20));
users.add(new User("Charlie", 22));
List<User> adultUsers = users.stream()
.filter(user -> user.getAge() > 18)
.collect(java.util.stream.Collectors.toList());
adultUsers.forEach(user -> System.out.println(user.getAge()));
}
}
映射元素
将一种类型的元素转换为另一种类型是常见的操作。例如,将字符串列表转换为整数列表:
import java.util.ArrayList;
import java.util.List;
public class MapExample {
public static void main(String[] args) {
List<String> numbers = new ArrayList<>();
numbers.add("1");
numbers.add("2");
numbers.add("3");
List<Integer> intNumbers = numbers.stream()
.map(Integer::parseInt)
.collect(java.util.stream.Collectors.toList());
System.out.println(intNumbers); // 输出: [1, 2, 3]
}
}
查找与匹配
查找流中是否存在满足条件的元素,或者查找第一个满足条件的元素:
import java.util.ArrayList;
import java.util.List;
public class FindExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
boolean exists = numbers.stream()
.anyMatch(n -> n > 3);
System.out.println(exists); // 输出: true
numbers.stream()
.filter(n -> n > 3)
.findFirst()
.ifPresent(System.out::println); // 输出: 4
}
}
归约操作
归约操作可以将流中的元素合并为一个值,例如计算列表中元素的总和、乘积等:
import java.util.ArrayList;
import java.util.List;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
int sum = numbers.stream()
.reduce(0, Integer::sum);
int product = numbers.stream()
.reduce(1, (a, b) -> a * b);
System.out.println("Sum: " + sum); // 输出: Sum: 15
System.out.println("Product: " + product); // 输出: Product: 120
}
}
最佳实践
性能优化
- 合理使用并行流:并行流适用于数据量较大且计算密集型的任务。但对于数据量较小或 I/O 密集型任务,并行流可能会引入额外的开销,反而降低性能。
- 避免不必要的中间操作:尽量减少链式调用中的中间操作,以减少计算量。
避免副作用
Stream 操作应该是无副作用的,即不应该修改流之外的可变状态。例如,在 forEach
中避免修改外部变量:
import java.util.ArrayList;
import java.util.List;
public class SideEffectExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
int[] sum = {0};
numbers.stream()
.forEach(n -> sum[0] += n); // 不推荐,有副作用
System.out.println(sum[0]); // 输出: 6
}
}
推荐使用 reduce
等纯函数式操作来计算结果。
正确选择操作
根据具体需求选择合适的 Stream 操作,避免过度使用复杂的操作。例如,如果只需要判断是否存在满足条件的元素,使用 anyMatch
比 filter
后再检查结果集是否为空更高效。
小结
List.stream
为 Java 开发者提供了一种强大而简洁的方式来处理 List
集合数据。通过掌握基础概念、使用方法、常见实践以及最佳实践,我们可以编写出更高效、易读和可维护的代码。在实际开发中,根据具体需求合理运用 Stream API,能够显著提升开发效率和代码质量。