【集合】Java 8 - Stream API 17种常用操作与案例详解

文章目录

  • [Java8 Stream API 17种常用操作与案例详解](#Java8 Stream API 17种常用操作与案例详解)
    • [1. collect():将流中的元素收集到集合中](#1. collect():将流中的元素收集到集合中)
    • [2. filter():根据条件过滤流中的元素](#2. filter():根据条件过滤流中的元素)
    • [3. map():元素映射为另一个值](#3. map():元素映射为另一个值)
    • [4. forEach():对流中的元素执行操作](#4. forEach():对流中的元素执行操作)
    • [5. flatMap():将流中的元素展开成一个流](#5. flatMap():将流中的元素展开成一个流)
    • [6. reduce():累积操作](#6. reduce():累积操作)
    • [7. distinct():去重](#7. distinct():去重)
    • [8. sorted():排序](#8. sorted():排序)
    • [9. limit():截断流](#9. limit():截断流)
    • [10. skip():跳过前 n 个元素](#10. skip():跳过前 n 个元素)
    • [11. anyMatch() / allMatch() / noneMatch():条件匹配](#11. anyMatch() / allMatch() / noneMatch():条件匹配)
    • [12. findFirst() 和 findAny()](#12. findFirst() 和 findAny())
    • [13. max() / min():获取最大值或最小值](#13. max() / min():获取最大值或最小值)
    • [14. peek():调试流中的元素](#14. peek():调试流中的元素)
    • [15. count():统计流中的元素个数](#15. count():统计流中的元素个数)
    • [16. groupBy():将流中的元素按照指定的条件分组](#16. groupBy():将流中的元素按照指定的条件分组)
    • [17. partitioningBy():将流中的元素按照指定的条件分成两个部分。](#17. partitioningBy():将流中的元素按照指定的条件分成两个部分。)
  • 总结补充

Java8 Stream API 17种常用操作与案例详解

在 Java 8 中,Stream API 提供了一种高效、简洁的数据处理方式,特别适合对集合、数组等数据源进行操作。Stream 通过函数式编程风格,支持链式调用,避免了传统 for 循环中复杂的代码逻辑。本文将详细介绍 17 种常用 Stream 操作方法,并通过示例代码帮助大家理解如何应用于实际开发中。


1. collect():将流中的元素收集到集合中

collect() 方法用于将流中的数据收集到集合、映射等数据结构中,非常常见。

示例

java 复制代码
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() 用于筛选出满足特定条件的元素,它返回一个新的流,流中的元素都满足过滤条件。

示例

java 复制代码
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() 会对流中的每个元素执行操作,并将结果映射成一个新的流。

示例

java 复制代码
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() 是终止操作,用于遍历流中的元素并执行指定的操作。

示例

java 复制代码
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),它将每个元素的流展开,并合并成一个新的流。

示例

java 复制代码
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() 用于将流中的元素进行聚合操作,例如求和、求最大值等。

示例

java 复制代码
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() 方法用于去掉流中的重复元素,最终返回一个去重后的流。

示例

java 复制代码
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() 可以对流中的元素进行排序,支持自然排序和自定义排序。

示例

java 复制代码
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 个元素。

示例

java 复制代码
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 个元素,返回剩余元素的流。

示例

java 复制代码
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()

java 复制代码
// 获取第 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():没有任何元素满足条件。

示例

java 复制代码
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():返回流中的任意一个元素,适合并行流操作,效率更高。
java 复制代码
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() 用于根据比较器找出流中的最大或最小元素。

示例

java 复制代码
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() 用于对流中的每个元素执行操作,但不会中断流的操作。适合用来调试。

示例

java 复制代码
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() 是一个终止操作,用于统计流中元素的个数。

示例

java 复制代码
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) 支持根据自定义逻辑对流中的元素进行排序。

示例

java 复制代码
 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) 支持根据自定义逻辑对流中的元素进行排序。

示例

java 复制代码
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]}

分区结合统计信息:

java 复制代码
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 在数据处理方面提供了强大的功能。总结一些常见的使用场景:

  1. 数据过滤与筛选filter()distinct()limit()
  2. 数据转换与聚合map()flatMap()reduce()
  3. 数据统计与校验count()allMatch()anyMatch()
  4. 调试与排序peek()sorted()max()min()

合理使用 Stream API,能够让代码更加简洁、可读性更强,同时提高开发效率。希望这篇文章对大家有帮助!!👍★(疯狂暗示)

博客主页: 总是学不会.

相关推荐
·薯条大王2 小时前
MySQL联合查询
数据库·mysql
战族狼魂2 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
niandb3 小时前
The Rust Programming Language 学习 (九)
windows·rust
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch4 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
天天向上杰5 小时前
面基JavaEE银行金融业务逻辑层处理金融数据类型BigDecimal
java·bigdecimal
请来次降维打击!!!5 小时前
优选算法系列(5.位运算)
java·前端·c++·算法
用键盘当武器的秋刀鱼6 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
嘤国大力士6 小时前
C++11&QT复习 (七)
java·c++·qt