文章目录
- Java8 Stream API 17种常用操作与案例详解
-
- 1. collect():将流中的元素收集到集合中
- 2. filter():根据条件过滤流中的元素
- 3. map():元素映射为另一个值
- 4. forEach():对流中的元素执行操作
- 5. flatMap():将流中的元素展开成一个流
- 6. reduce():累积操作
- 7. distinct():去重
- 8. sorted():排序
- 9. limit():截断流
- 10. skip():跳过前 n 个元素
- 11. anyMatch() / allMatch() / noneMatch():条件匹配
- 12. findFirst() 和 findAny()
- 13. max() / min():获取最大值或最小值
- 14. peek():调试流中的元素
- 15. count():统计流中的元素个数
- 16. groupBy():将流中的元素按照指定的条件分组
- 17. partitioningBy():将流中的元素按照指定的条件分成两个部分。
- 总结补充
Java8 Stream API 17种常用操作与案例详解
在 Java 8 中,Stream API 提供了一种高效、简洁的数据处理方式,特别适合对集合、数组等数据源进行操作。Stream 通过函数式编程风格,支持链式调用,避免了传统 for 循环中复杂的代码逻辑。本文将详细介绍 17 种常用 Stream 操作方法,并通过示例代码帮助大家理解如何应用于实际开发中。
1. collect():将流中的元素收集到集合中
collect()
方法用于将流中的数据收集到集合、映射等数据结构中,非常常见。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 收集名字长度大于 3 的元素到 List 集合
List<String> filteredNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
System.out.println(filteredNames); // Output: [Alice, Charlie, David]
理解要点:
collect()
是终止操作,它将流中的元素重新汇总到新的集合中。这里的 Collectors.toList()
是一种常见用法,用于收集元素到 List 集合中。
2. filter():根据条件过滤流中的元素
filter()
用于筛选出满足特定条件的元素,它返回一个新的流,流中的元素都满足过滤条件。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 过滤出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // Output: [2, 4]
理解要点:
这里 n -> n % 2 == 0
是一个 Lambda 表达式,表示只保留能被 2 整除的元素。filter()
不会修改原数据,而是返回一个新的流。
3. map():元素映射为另一个值
map()
会对流中的每个元素执行操作,并将结果映射成一个新的流。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 转换为大写
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseNames); // Output: [ALICE, BOB, CHARLIE]
理解要点:
map()
接受一个函数作为参数,将流中的每个元素转换为新形式。String::toUpperCase
是方法引用,等同于name -> name.toUpperCase()
。
4. forEach():对流中的元素执行操作
forEach()
是终止操作,用于遍历流中的元素并执行指定的操作。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 输出每个元素
numbers.stream().forEach(System.out::println);
// Output: 1 2 3 4 5
理解要点:
System.out::println
是方法引用,等同于n -> System.out.println(n)
。forEach()
不返回新流,只执行操作。
5. flatMap():将流中的元素展开成一个流
flatMap()
用于处理嵌套集合(比如 List),它将每个元素的流展开,并合并成一个新的流。
示例:
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8)
);
// 合并多个列表为一个流
List<Integer> mergedList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(mergedList); // Output: [1, 2, 3, 4, 5, 6, 7, 8]
理解要点:
flatMap()
的作用是 “摊平” 元素,比如将多个小集合合并成一个大集合。- 这里
List::stream
表示将每个子集合转换为流。
6. reduce():累积操作
reduce()
用于将流中的元素进行聚合操作,例如求和、求最大值等。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // Output: 15
理解要点:
reduce
需要一个初始值(这里是0
),一个二元操作(Integer::sum
)。- 操作会从初始值开始累加流中的元素。
7. distinct():去重
distinct()
方法用于去掉流中的重复元素,最终返回一个去重后的流。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 4);
// 去重
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctNumbers); // Output: [1, 2, 3, 4]
理解要点:
distinct()
会基于元素的 equals()
和 hashCode()
方法判断是否重复。
8. sorted():排序
sorted()
可以对流中的元素进行排序,支持自然排序和自定义排序。
示例:
List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
// 升序排序
List<Integer> sortedNumbers = numbers.stream()
.sorted() //Comparator.comparing()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // Output: [1, 2, 3, 4, 5]
理解要点:
- 默认情况下,
sorted()
使用自然排序(升序)。 - 如果需要自定义排序,可以传入比较器。
9. limit():截断流
limit()
用于获取流中的前 n 个元素。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// 只获取前 3 个元素
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println(limitedNumbers); // Output: [1, 2, 3]
实际场景:
例如分页功能中,可以结合 skip()
实现数据的分页查询。
10. skip():跳过前 n 个元素
skip()
用于跳过流中的前 n 个元素,返回剩余元素的流。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// 跳过前 4 个元素
List<Integer> skippedNumbers = numbers.stream()
.skip(4)
.collect(Collectors.toList());
System.out.println(skippedNumbers); // Output: [5, 6, 7]
结合 limit():
// 获取第 4 到第 6 个元素
List<Integer> subList = numbers.stream()
.skip(3)
.limit(3)
.collect(Collectors.toList());
System.out.println(subList); // Output: [4, 5, 6]
实际场景:
跳过已处理数据,优化批量处理的效率。
11. anyMatch() / allMatch() / noneMatch():条件匹配
这三个方法用于判断流中的元素是否符合条件:
anyMatch()
:是否有任意一个元素满足条件。allMatch()
:所有元素都满足条件。noneMatch()
:没有任何元素满足条件。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 是否存在大于 4 的元素
boolean anyMatch = numbers.stream().anyMatch(n -> n > 4);
System.out.println(anyMatch); // Output: true
// 是否所有元素都小于 6
boolean allMatch = numbers.stream().allMatch(n -> n < 6);
System.out.println(allMatch); // Output: true
// 是否没有大于 5 的元素
boolean noneMatch = numbers.stream().noneMatch(n -> n > 5);
System.out.println(noneMatch); // Output: true
实际场景:
- 数据校验,例如用户输入是否符合条件。
- 筛选判断,例如是否存在符合条件的订单。
12. findFirst() 和 findAny()
findFirst()
:返回流中的第一个元素,通常在顺序流中使用。findAny()
:返回流中的任意一个元素,适合并行流操作,效率更高。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用 findFirst 方法查找第一个元素
Optional<Integer> first = numbers.stream().findFirst();
// 使用 findAny 方法查找任意一个元素
Optional<Integer> any = numbers.stream().findAny();
// 输出结果
System.out.println("First: " + first.orElse(null)); // Output: 1
System.out.println("Any: " + any.orElse(null)); // Output: 1 或其他任意元素
特别说明:
- 在 顺序流 中,
findAny()
的行为与findFirst()
相似,通常会返回第一个元素。 - 在 并行流 中,
findAny()
可能会返回流中任意位置的元素,以提高性能。
13. max() / min():获取最大值或最小值
max()
和 min()
用于根据比较器找出流中的最大或最小元素。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 获取最大值
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
System.out.println(max.get()); // Output: 5
// 获取最小值
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
System.out.println(min.get()); // Output: 1
理解要点:
max()
和min()
返回Optional
对象,需使用.get()
获取值。- 可以使用自定义比较器实现复杂比较逻辑。
14. peek():调试流中的元素
peek()
用于对流中的每个元素执行操作,但不会中断流的操作。适合用来调试。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 打印调试信息
List<String> filteredNames = names.stream()
.peek(name -> System.out.println("Before filter: " + name))
.filter(name -> name.length() > 3)
.peek(name -> System.out.println("After filter: " + name))
.collect(Collectors.toList());
System.out.println(filteredNames);
输出:
Before filter: Alice
After filter: Alice
Before filter: Bob
Before filter: Charlie
After filter: Charlie
[Alice, Charlie]
理解要点:
peek()
通常用于调试,打印中间状态,帮助排查问题。
15. count():统计流中的元素个数
count()
是一个终止操作,用于统计流中元素的个数。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 统计名字长度大于 3 的元素个数
long count = names.stream()
.filter(name -> name.length() > 3)
.count();
System.out.println(count); // Output: 2
实际场景:
数据统计,比如计算符合条件的记录数量。
16. groupBy():将流中的元素按照指定的条件分组
sorted(Comparator)
支持根据自定义逻辑对流中的元素进行排序。
示例:
List<String> words = Arrays.asList("dog", "cat", "elephant", "rat", "ant");
// 按字符串长度分组
Map<Integer, List<String>> groupedByLength = words.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength);
// 输出: {3=[dog, cat, rat, ant], 8=[elephant]}
理解要点:
Collectors.groupingBy()
返回值作为键。
17. partitioningBy():将流中的元素按照指定的条件分成两个部分。
sorted(Comparator)
支持根据自定义逻辑对流中的元素进行排序。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 根据是否为偶数进行分区
Map<Boolean, List<Integer>> partitionedByEvenOdd = numbers.stream()
.collect(Collectors.partitioningBy(num -> num % 2 == 0));
System.out.println(partitionedByEvenOdd);
// 输出: {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8]}
分区结合统计信息:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 分区后统计每组的元素数量
Map<Boolean, Long> countByEvenOdd = numbers.stream()
.collect(Collectors.partitioningBy(num -> num % 2 == 0, Collectors.counting()));
System.out.println(countByEvenOdd);
// 输出: {false=5, true=4}
总结补充
通过以上 17 个常用操作,我们可以看到 Stream API 在数据处理方面提供了强大的功能。总结一些常见的使用场景:
- 数据过滤与筛选:
filter()
、distinct()
、limit()
。 - 数据转换与聚合:
map()
、flatMap()
、reduce()
。 - 数据统计与校验:
count()
、allMatch()
、anyMatch()
。 - 调试与排序:
peek()
、sorted()
、max()
、min()
。
合理使用 Stream API,能够让代码更加简洁、可读性更强,同时提高开发效率。希望这篇文章对大家有帮助!!👍★(疯狂暗示)
博客主页: 总是学不会.