Java8_Stream_API_总结
Java 8 Stream API 全面总结
1. Stream API 简介
Stream(流)是 Java 8 引入的一个重要特性,它提供了一种函数式编程的方式来处理集合数据。Stream API 允许以声明式方式处理数据集合,使代码更简洁、可读性更强,并且能够利用多核架构进行并行计算。
Stream 的特点:
- 非存储的数据结构:Stream 不存储元素,而是按需计算
- 函数式编程:强调无状态操作和不可变性
- 惰性执行:中间操作不会立即执行,而是等到终端操作时才执行
- 可能一次性使用:Stream 通常只能遍历一次
- 内部迭代:Stream 操作内部完成迭代,而不是像集合那样需要显式迭代
2. 创建 Stream
从集合创建
List<String> list = Arrays.asList("张三", "李四", "王五");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // 并行流
从数组创建
String[] array = {"张三", "李四", "王五"};
Stream<String> stream = Arrays.stream(array);
Stream<String> partStream = Arrays.stream(array, 0, 2); // 部分数组
使用 Stream.of() 方法
Stream<String> stream = Stream.of("张三", "李四", "王五");
创建无限流
// 生成从0开始的无限整数流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
// 使用limit限制为前10个元素
infiniteStream.limit(10).forEach(System.out::println);
// 生成随机数流
Stream<Double> randomStream = Stream.generate(Math::random);
randomStream.limit(5).forEach(System.out::println);
3. 中间操作(Intermediate Operations)
中间操作返回一个新的 Stream,可以链式调用多个中间操作。中间操作是惰性执行的。
筛选和切片
// filter: 过滤元素
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
// distinct: 去重
list.stream().distinct().forEach(System.out::println);
// limit: 限制数量
list.stream().limit(2).forEach(System.out::println);
// skip: 跳过元素
list.stream().skip(1).forEach(System.out::println);
映射
// map: 一对一映射转换
list.stream().map(String::length).forEach(System.out::println);
// flatMap: 一对多映射转换,常用于处理嵌套集合
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4)
);
nestedList.stream()
.flatMap(Collection::stream)
.forEach(System.out::println); // 输出 1, 2, 3, 4
排序
// 自然排序
list.stream().sorted().forEach(System.out::println);
// 自定义排序
list.stream().sorted(Comparator.comparing(String::length)).forEach(System.out::println);
list.stream().sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
窥视(不改变流)
// peek: 用于调试,不改变流
list.stream()
.peek(e -> System.out.println("原始元素: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("转换后: " + e))
.forEach(System.out::println);
4. 终端操作(Terminal Operations)
终端操作触发流的执行并返回结果。执行终端操作后,流将被消费,不能再使用。
匹配和查找
// anyMatch: 至少一个元素匹配
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("张"));
// allMatch: 所有元素都匹配
boolean allMatch = list.stream().allMatch(s -> s.length() > 1);
// noneMatch: 没有元素匹配
boolean noneMatch = list.stream().noneMatch(s -> s.endsWith("六"));
// findFirst: 返回第一个元素
Optional<String> first = list.stream().findFirst();
// findAny: 返回任意元素(在并行流中很有用)
Optional<String> any = list.stream().findAny();
归约
// reduce: 将流中元素归约为一个值
Optional<String> reduced = list.stream().reduce((s1, s2) -> s1 + ", " + s2);
System.out.println(reduced.orElse("")); // 输出: 张三, 李四, 王五
// 带初始值的reduce
String reducedWithIdentity = list.stream().reduce("前缀: ", (s1, s2) -> s1 + s2);
System.out.println(reducedWithIdentity); // 输出: 前缀: 张三李四王五
// 计算数值和
int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
System.out.println(sum); // 输出: 15
收集
// collect: 将流转换为其他形式
List<String> collectedList = list.stream().collect(Collectors.toList());
Set<String> collectedSet = list.stream().collect(Collectors.toSet());
String joined = list.stream().collect(Collectors.joining(", "));
System.out.println(joined); // 输出: 张三, 李四, 王五
// 分组
Map<Integer, List<String>> groupByLength = list.stream()
.collect(Collectors.groupingBy(String::length));
// 分区
Map<Boolean, List<String>> partitioned = list.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 2));
// 统计
IntSummaryStatistics stats = list.stream()
.collect(Collectors.summarizingInt(String::length));
System.out.println("平均长度: " + stats.getAverage());
System.out.println("最大长度: " + stats.getMax());
遍历
// forEach: 遍历每个元素
list.stream().forEach(System.out::println);
// forEachOrdered: 保证按顺序遍历(在并行流中很重要)
list.parallelStream().forEachOrdered(System.out::println);
计数
// count: 返回流中元素的个数
long count = list.stream().count();
System.out.println("元素个数: " + count);
5. 实用示例
示例1:过滤和转换员工数据
class Employee {
private String name;
private int age;
private double salary;
// 构造函数、getter和setter省略...
}
List<Employee> employees = Arrays.asList(
new Employee("张三", 30, 10000),
new Employee("李四", 25, 8000),
new Employee("王五", 35, 12000),
new Employee("赵六", 40, 15000)
);
// 获取所有年龄大于30的员工姓名,并按字母排序
List<String> names = employees.stream()
.filter(e -> e.getAge() > 30)
.map(Employee::getName)
.sorted()
.collect(Collectors.toList());
System.out.println(names); // 输出: [王五, 赵六]
// 计算所有员工的平均薪资
double averageSalary = employees.stream()
.mapToDouble(Employee::getSalary)
.average()
.orElse(0.0);
System.out.println("平均薪资: " + averageSalary);
示例2:处理文本
String text = "Stream API是Java 8引入的新特性,它提供了函数式编程的能力";
// 统计单词数(按空格分割)
long wordCount = Arrays.stream(text.split("\\s+")).count();
System.out.println("单词数: " + wordCount);
// 获取最长的单词
String longestWord = Arrays.stream(text.split("\\s+"))
.max(Comparator.comparing(String::length))
.orElse("");
System.out.println("最长的单词: " + longestWord);
示例3:数值流的特殊操作
// IntStream, LongStream, DoubleStream
IntStream intStream = IntStream.range(1, 10); // 生成1到9的整数流
int sum = intStream.sum();
System.out.println("总和: " + sum);
// 统计
IntStream.of(1, 3, 5, 7, 9)
.summaryStatistics()
.forEach(stats -> {
System.out.println("总和: " + stats.getSum());
System.out.println("平均值: " + stats.getAverage());
System.out.println("最大值: " + stats.getMax());
System.out.println("最小值: " + stats.getMin());
System.out.println("数量: " + stats.getCount());
});
6. Stream API 最佳实践
-
优先使用流的方法而非迭代:流操作通常更简洁、可读性更高
-
合理使用并行流:
- 数据量大且每个元素处理开销大时使用并行流
- 注意避免使用并行流处理有状态操作或者对顺序敏感的操作
- 默认并行流使用 ForkJoinPool.commonPool(),可能影响系统其他部分
// 适合并行的场景 List<BigInteger> numbers = getVeryLargeList(); numbers.parallelStream() .map(this::complexCalculation) .collect(Collectors.toList());
-
使用恰当的收集器:选择合适的 Collectors 方法可以大幅提高代码效率
-
避免过度使用流:简单任务可能使用传统循环更清晰
-
注意流的惰性执行:理解中间操作不会立即执行的特性
-
调试技巧:使用 peek() 方法可以在不改变流的情况下调试中间结果
-
避免副作用:保持流操作的纯函数特性,避免修改外部状态
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小刘
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果