Java Stream API 详解
掌握 Stream 函数式编程,提升集合数据处理效率
目录
什么是 Stream
Stream 是 Java 8 引入的函数式编程特性,用于对集合数据进行高效、声明式的操作。
特点:
- 链式操作,代码简洁
- 支持并行处理
- 惰性求值,性能优化
- 不修改原数据源
创建 Stream
import java.util.stream.*;
import java.util.*;
// 1. 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
Stream<String> parallelStream = list.parallelStream();
// 2. 从数组创建
Stream<String> stream2 = Arrays.stream(new String[]{"a", "b", "c"});
// 3. 使用 Stream.of
Stream<String> stream3 = Stream.of("a", "b", "c");
// 4. 无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2).limit(10);
Stream<Double> randomStream = Stream.generate(Math::random).limit(5);
// 5. 从文件创建(IO流)
Stream<String> lines = Files.lines(Paths.get("file.txt"));
// 6. 空流
Stream<String> emptyStream = Stream.empty();中间操作(Intermediate Operations)
中间操作返回新的 Stream,可以链式调用。
filter - 过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// [2, 4, 6]
// 过滤非空字符串
List<String> nonEmpty = strings.stream()
.filter(s -> s != null && !s.isEmpty())
.collect(Collectors.toList());map - 映射转换
// 转换为大写
List<String> upperCase = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 提取属性
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 对象转换
List<String> userNames = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// flatMap - 扁平化
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6]sorted - 排序
// 自然排序
List<String> sorted = names.stream()
.sorted()
.collect(Collectors.toList());
// 自定义排序
List<User> sortedByAge = users.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());
// 倒序
List<User> sortedDesc = users.stream()
.sorted(Comparator.comparing(User::getAge).reversed())
.collect(Collectors.toList());
// 多级排序
List<User> sortedMulti = users.stream()
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());distinct - 去重
List<Integer> unique = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 根据属性去重(需要自定义)
List<User> uniqueUsers = users.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(User::getId))),
ArrayList::new
));limit 和 skip - 分页
List<Integer> limited = numbers.stream()
.limit(3) // 取前3个
.collect(Collectors.toList());
List<Integer> skipped = numbers.stream()
.skip(3) // 跳过前3个
.collect(Collectors.toList());
// 分页实现
int page = 1;
int size = 10;
List<User> pageData = users.stream()
.skip((long) (page - 1) * size)
.limit(size)
.collect(Collectors.toList());peek - 调试
List<Integer> result = numbers.stream()
.peek(n -> System.out.println("处理前: " + n))
.map(n -> n * 2)
.peek(n -> System.out.println("处理后: " + n))
.collect(Collectors.toList());终止操作(Terminal Operations)
forEach - 遍历
names.stream()
.forEach(System.out::println);
// 带索引的遍历
AtomicInteger index = new AtomicInteger(0);
names.stream()
.forEach(name -> System.out.println(index.getAndIncrement() + ": " + name));collect - 收集
// 转为 List
List<String> list = stream.collect(Collectors.toList());
// 转为 Set
Set<String> set = stream.collect(Collectors.toSet());
// 转为特定集合
LinkedList<String> linkedList = stream.collect(
Collectors.toCollection(LinkedList::new));
// 转为 Map
Map<Long, String> idNameMap = users.stream()
.collect(Collectors.toMap(User::getId, User::getName));
// 处理键冲突
Map<String, Integer> nameAgeMap = users.stream()
.collect(Collectors.toMap(
User::getName,
User::getAge,
(existing, replacement) -> existing // 保留旧值
));
// 分组
Map<String, List<User>> groupByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
// 多级分组
Map<String, Map<String, List<User>>> groupByCityAndGender = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.groupingBy(User::getGender)
));
// 分区(二分组)
Map<Boolean, List<User>> partitionByAge = users.stream()
.collect(Collectors.partitioningBy(u -> u.getAge() >= 18));
// 统计
IntSummaryStatistics stats = numbers.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(stats.getAverage()); // 平均数
System.out.println(stats.getSum()); // 总和
System.out.println(stats.getMax()); // 最大值
System.out.println(stats.getMin()); // 最小值
// 连接字符串
String joined = names.stream()
.collect(Collectors.joining(", ", "[", "]"));
// [Alice, Bob, Charlie]
// 自定义收集器
String result = stream.collect(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append
).toString();reduce - 归约
// 求和
int sum = numbers.stream()
.reduce(0, Integer::sum);
// 阶乘
OptionalInt factorial = IntStream.rangeClosed(1, 5)
.reduce((a, b) -> a * b);
// 120
// 字符串连接
String concatenated = names.stream()
.reduce("", (a, b) -> a + " " + b);
// 最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);匹配与查找
// anyMatch - 是否包含
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
// allMatch - 是否全部满足
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0);
// noneMatch - 是否全部不满足
boolean noNegative = numbers.stream()
.noneMatch(n -> n < 0);
// findFirst - 查找第一个
Optional<String> first = names.stream()
.filter(s -> s.startsWith("A"))
.findFirst();
// findAny - 查找任意一个(并行流更高效)
Optional<String> any = names.parallelStream()
.filter(s -> s.length() > 3)
.findAny();统计
long count = stream.count();
Optional<T> max = stream.max(Comparator.naturalOrder());
Optional<T> min = stream.min(Comparator.naturalOrder());数值流
// IntStream
int sum = IntStream.of(1, 2, 3, 4, 5).sum();
double average = IntStream.range(1, 100).average().orElse(0);
IntSummaryStatistics stats = IntStream.of(1, 2, 3).summaryStatistics();
// 对象流转数值流
int totalAge = users.stream()
.mapToInt(User::getAge)
.sum();
// 数值流转对象流
Stream<Integer> boxedStream = IntStream.of(1, 2, 3).boxed();并行流
// 创建并行流
List<Integer> result = numbers.parallelStream()
.map(n -> heavyComputation(n))
.collect(Collectors.toList());
// 普通流转并行流
List<Integer> result = numbers.stream()
.parallel()
.map(n -> heavyComputation(n))
.collect(Collectors.toList());
// 适合并行流的场景:
// 1. 数据量大(通常 > 10,000)
// 2. 复杂计算
// 3. 无状态、无副作用实际应用示例
示例 1:数据处理
// 获取年龄大于18岁的用户名,按年龄排序
List<String> result = users.stream()
.filter(u -> u.getAge() > 18)
.sorted(Comparator.comparing(User::getAge))
.map(User::getName)
.distinct()
.limit(10)
.collect(Collectors.toList());示例 2:分组统计
// 按部门统计员工平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));示例 3:文件处理
// 读取文件,统计单词频率
Map<String, Long> wordCount = Files.lines(Paths.get("file.txt"))
.flatMap(line -> Arrays.stream(line.split("\\s+")))
.map(String::toLowerCase)
.filter(word -> !word.isEmpty())
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));示例 4:Optional 链式操作
// 获取第一个成年用户的名字
String name = users.stream()
.filter(u -> u.getAge() >= 18)
.findFirst()
.map(User::getName)
.orElse("Unknown");注意事项
- Stream 只能消费一次
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 报错!- 避免在 Stream 中修改外部变量
// 错误
List<Integer> result = new ArrayList<>();
numbers.forEach(result::add);
// 正确
List<Integer> result = numbers.stream()
.collect(Collectors.toList());- 谨慎使用并行流
- 数据量小反而更慢
- 涉及 I/O 可能阻塞 ForkJoinPool
- 注意线程安全问题
总结
Stream API 是 Java 函数式编程的核心特性,掌握它可以写出更简洁、高效的代码。关键点:
- 区分中间操作和终止操作
- 熟悉常用收集器
- 合理使用并行流
- 注意流的一次性消费特性