一、Stream 流的概述
Java Stream 是 Java 8 引入的一个重要的功能,它允许你以声明性的方式对集合或其他数据源进行操作。Stream 流提供了一种高效、易读且功能强大的方式来处理数据集合,支持顺序和并行两种处理方式。
二、Stream 流的常见用法
1. 创建 Stream 流
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
// 从数组创建
String[] array = new String[]{"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);
// 使用静态方法 Stream.of()
Stream<String> streamOf = Stream.of("a", "b", "c");
2. 筛选过滤
// filter() 方法用于筛选符合条件的元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// evenNumbers 结果为 [2, 4, 6]
3. 映射转换
// map() 方法用于将流中的元素映射为另一种类型
List<String> words = Arrays.asList("hello", "world", "java");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// lengths 结果为 [5, 5, 4]
4. 聚合操作
// 使用 reduce() 进行聚合操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
// sum 结果为 15
// 使用收集器 Collectors 进行聚合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.collect(Collectors.summingInt(n -> n));
// sum 结果为 15
5. 排序
// sorted() 方法对流中的元素进行排序
List<Integer> numbers = Arrays.asList(5, 3, 2, 4, 1);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// sortedNumbers 结果为 [1, 2, 3, 4, 5]
// 自定义排序
List<String> words = Arrays.asList("banana", "apple", "orange");
List<String> sortedWords = words.stream()
.sorted((a, b) -> a.length() - b.length())
.collect(Collectors.toList());
// sortedWords 结果为 ["apple", "banana", "orange"]
6. 去重
// distinct() 方法用于去除重复的元素
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> uniqueNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// uniqueNumbers 结果为 [1, 2, 3, 4]
7. 限制和跳过
// limit() 方法限制流中元素的数量
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// limitedNumbers 结果为 [1, 2, 3]
// skip() 方法跳过流中的前 n 个元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// skippedNumbers 结果为 [3, 4, 5]
8. 匹配和查找
// anyMatch() 检查是否至少有一个元素符合给定的条件
List<String> words = Arrays.asList("hello", "world", "java");
boolean hasLongWord = words.stream()
.anyMatch(word -> word.length() > 5);
// hasLongWord 结果为 true
// allMatch() 检查所有元素是否都符合给定的条件
List<Integer> numbers = Arrays.asList(2, 4, 6, 8);
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0);
// allEven 结果为 true
// noneMatch() 检查没有元素符合给定的条件
List<Integer> numbers = Arrays.asList(1, 3, 5, 7);
boolean noneEven = numbers.stream()
.noneMatch(n -> n % 2 == 0);
// noneEven 结果为 true
// findFirst() 返回流中的第一个元素
List<String> words = Arrays.asList("hello", "world", "java");
Optional<String> firstWord = words.stream()
.findFirst();
// firstWord 结果为 Optional["hello"]
// findAny() 返回流中的任意一个元素(在并行流中可能更高效)
List<String> words = Arrays.asList("hello", "world", "java");
Optional<String> anyWord = words.stream()
.findAny();
// anyWord 结果可能为 Optional["hello"] 或其他元素
9. 流的扁平化
// flatMap() 将流中的每个元素映射为另一个流,然后将这些流连接成一个流
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// flatList 结果为 [1, 2, 3, 4, 5, 6]
10. 转换为其他集合形式
// 转换为 List
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredList = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// squaredList 结果为 [1, 4, 9, 16, 25]
// 转换为 Set
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
Set<Integer> uniqueSet = numbers.stream()
.collect(Collectors.toSet());
// uniqueSet 结果为 {1, 2, 3, 4}
// 转换为 Map
List<String> words = Arrays.asList("hello", "world", "java");
Map<String, Integer> wordLengthMap = words.stream()
.collect(Collectors.toMap(word -> word, word -> word.length()));
// wordLengthMap 结果为 {"hello"=5, "world"=5, "java"=4}
三、Stream 流的常见场景
1. 数据处理
// 计算员工工资总和
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000),
new Employee("Bob", 6000),
new Employee("Charlie", 7000)
);
int totalSalary = employees.stream()
.mapToInt(Employee::getSalary)
.sum();
// totalSalary 结果为 18000
2. 集合操作优化
// 找出最长的单词
List<String> words = Arrays.asList("hello", "world", "java");
String longestWord = words.stream()
.max((a, b) -> a.length() - b.length())
.orElse("");
// longestWord 结果为 "hello"
3. 函数式编程实践
// 使用函数式接口对数字进行平方操作
Function<Integer, Integer> square = x -> x * x;
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream()
.map(square)
.collect(Collectors.toList());
// squaredNumbers 结果为 [1, 4, 9, 16, 25]
四、Stream 流的注意事项
1. 避免在 Stream 操作中产生副作用
// 不推荐:在 Stream 操作中修改外部变量
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredList = new ArrayList<>();
numbers.stream()
.forEach(n -> squaredList.add(n * n));
// squaredList 结果为 [1, 4, 9, 16, 25]
// 推荐:使用 map() 和 collect() 进行无副作用的操作
List<Integer> squaredList = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
2. 注意 Stream 的延迟执行特性
// Stream 操作是延迟执行的,只有在终止操作时才会真正执行
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream()
.filter(n -> n % 2 == 0);
// 此时还没有执行过滤操作
List<Integer> evenNumbers = stream.collect(Collectors.toList());
// 此时才真正执行过滤操作
3. 处理异常和错误
// 在 Stream 操作中处理异常
List<String> words = Arrays.asList("hello", "world", "java");
List<Integer> lengths = words.stream()
.map(word -> {
try {
return word.length();
} catch (Exception e) {
return 0;
}
})
.collect(Collectors.toList());
4. 注意 Stream 的不可重复使用性
// Stream 只能使用一次,再次使用会抛出异常
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
stream.filter(n -> n % 2 == 0).collect(Collectors.toList());
// stream.filter(n -> n % 2 == 0).collect(Collectors.toList()); // 再次使用会抛出异常
5. 注意并行流的使用
// 并行流可以提高处理性能,但需要注意线程安全和顺序问题
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredList = numbers.parallelStream()
.map(n -> n * n)
.collect(Collectors.toList());
// squaredList 结果可能为 [1, 4, 9, 16, 25],但顺序可能不保证
6. 避免过度使用 Stream
// 对于简单操作,传统的循环可能更高效
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 不推荐:过度使用 Stream
List<Integer> squaredList = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 推荐:对于简单操作,直接使用循环
List<Integer> squaredList = new ArrayList<>();
for (int n : numbers) {
squaredList.add(n * n);
}
7. 注意空指针异常
// 在 Stream 操作中处理可能的空值
List<String> words = Arrays.asList("hello", null, "java");
List<String> nonNullWords = words.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
// nonNullWords 结果为 ["hello", "java"]
8. 注意收集器的使用
// 使用收集器时注意线程安全和性能问题
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> partitionedMap = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// partitionedMap 结果为 {false=[1, 3, 5], true=[2, 4]}
9. 注意流的中间操作和终止操作
// 中间操作不会执行任何处理,直到遇到终止操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream()
.filter(n -> n % 2 == 0);
// 此时还没有执行过滤操作
List<Integer> evenNumbers = stream.collect(Collectors.toList());
// 此时才真正执行过滤操作
10. 注意流的源数据不可变性
// Stream 操作不会修改源数据,除非显式地进行修改
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredList = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// numbers 列表保持不变,squaredList 是新的列表
总结
Java Stream 流提供了一种高效、易读且功能强大的方式来处理数据集合。通过筛选过滤、映射转换、聚合操作、排序、去重、限制和跳过、匹配和查找、流的扁平化以及转换为其他集合形式等常见用法,可以方便地对数据进行各种操作。在实际应用中,需要注意避免副作用、处理异常、注意延迟执行特性、避免过度使用 Stream 等事项,以确保代码的正确性和高效性。