Java Stream API是Java 8引入的最重要的新特性之一,它提供了一种声明式的方式来处理集合数据。本文将深入讲解Stream API的核心概念、常用操作和实战案例,助你写出更优雅、更高效的代码。
一、Stream API核心概念
Stream API是什么?简单来说,它是一个来自数据源的元素队列,支持顺序和并行聚合操作。与传统集合不同,Stream不存储数据,而是通过管道操作对数据进行计算。
Stream的特点
- 不存储数据: Stream不是数据结构,不存储元素
- 不可变性: Stream操作不会修改数据源
- 惰性求值: 中间操作不会立即执行,只有终止操作才会触发
- 可以并行: Stream支持并行处理,充分利用多核CPU
Stream vs 传统集合
// 传统方式:遍历List过滤
List<String> result = new ArrayList<>();
for (String str : names) {
if (str.startsWith("J")) {
result.add(str);
}
}
// Stream方式:声明式编程
List<String> result = names.stream()
.filter(str -> str.startsWith("J"))
.collect(Collectors.toList());
二、Stream的创建方式
1. 从集合创建
List<String> list = Arrays.asList("Java", "Stream", "API");
Stream<String> stream = list.stream();
2. 从数组创建
String[] array = {"Java", "Stream", "API"};
Stream<String> stream = Arrays.stream(array);
3. 使用Stream.of()
Stream<String> stream = Stream.of("Java", "Stream", "API");
4. 使用Stream.generate()
// 生成无限流
Stream<Double> randomStream = Stream.generate(Math::random).limit(10);
5. 使用Stream.iterate()
// 生成序列流
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
三、Stream常用操作详解
3.1 中间操作(Intermediate Operations)
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());
// 结果: [2, 4, 6]
map - 转换元素
List<String> names = Arrays.asList("java", "stream", "api");
// 转换为大写
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 结果: ["JAVA", "STREAM", "API"]
flatMap - 扁平化映射
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
// 扁平化为单个List
List<String> flattened = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果: ["a", "b", "c", "d"]
distinct - 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 4);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4]
sorted - 排序
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
// 自然排序
List<Integer> sorted = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 自定义排序
List<Integer> descSorted = numbers.stream()
.sorted((a, b) -> b.compareTo(a))
.collect(Collectors.toList());
limit - 截断
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 取前3个
List<Integer> first3 = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 结果: [1, 2, 3]
skip - 跳过
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 跳过前3个
List<Integer> afterSkip = numbers.stream()
.skip(3)
.collect(Collectors.toList());
// 结果: [4, 5, 6]
3.2 终止操作(Terminal Operations)
forEach - 遍历
List<String> names = Arrays.asList("Java", "Stream", "API");
names.stream()
.forEach(name -> System.out.println(name));
collect - 收集结果
List<String> names = Arrays.asList("Java", "Stream", "API");
// 收集为List
List<String> list = names.stream()
.collect(Collectors.toList());
// 收集为Set
Set<String> set = names.stream()
.collect(Collectors.toSet());
// 收集为Map
Map<String, Integer> map = names.stream()
.collect(Collectors.toMap(
name -> name,
String::length
));
reduce - 归约
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream()
.reduce(0, Integer::sum);
// 结果: 15
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
count - 计数
List<String> names = Arrays.asList("Java", "Stream", "API");
long count = names.stream()
.filter(name -> name.startsWith("J"))
.count();
// 结果: 1
anyMatch / allMatch / noneMatch - 匹配
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 是否存在偶数
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
// 是否全部为正数
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0);
四、实战案例
案例1: 处理用户数据
class User {
private String name;
private int age;
private String city;
// getter和setter
}
List<User> users = Arrays.asList(
new User("张三", 25, "北京"),
new User("李四", 30, "上海"),
new User("王五", 28, "北京")
);
// 查找北京的用户
List<String> beijingUsers = users.stream()
.filter(user -> "北京".equals(user.getCity()))
.map(User::getName)
.collect(Collectors.toList());
// 按年龄分组
Map<Integer, List<User>> usersByAge = users.stream()
.collect(Collectors.groupingBy(User::getAge));
案例2: 统计单词频率
String text = "Java Stream API is powerful Stream API makes code elegant";
Map<String, Long> wordCount = Arrays.stream(text.split(" "))
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
案例3: 数值操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 计算平均值
double average = numbers.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0);
// 查找最大值
int max = numbers.stream()
.mapToInt(Integer::intValue)
.max()
.orElse(0);
// 求和
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
五、并行Stream(Parallel Stream)
并行Stream可以利用多核CPU提升性能:
List<Integer> largeList = ...; // 大数据集
// 顺序流
long sequentialTime = measureTime(() -> {
largeList.stream()
.filter(n -> n % 2 == 0)
.count();
});
// 并行流
long parallelTime = measureTime(() -> {
largeList.parallelStream()
.filter(n -> n % 2 == 0)
.count();
});
注意: 并行流适合数据量大、计算密集的场景,小数据集可能更慢。
六、Stream最佳实践
1. 避免空指针
// 不好的做法
list.stream().forEach(...); // 可能为null
// 好的做法
Optional.ofNullable(list)
.orElse(Collections.emptyList())
.stream()
.forEach(...);
2. 使用方法引用
// 不好的做法
names.stream().map(name -> name.toUpperCase());
// 好的做法
names.stream().map(String::toUpperCase);
3. 合并操作
// 不好的做法
Stream<String> stream = names.stream();
stream = stream.filter(...);
stream = stream.map(...);
// 好的做法
names.stream()
.filter(...)
.map(...)
.collect(...);
4. 注意性能
- 避免在lambda中修改外部变量
- 大数据集考虑使用parallelStream
- 复杂操作考虑使用传统循环
七、常见问题
Q1: Stream和Iterator有什么区别?
A: Stream是Java 8的新特性,支持声明式编程、惰性求值、并行处理;Iterator是传统的迭代方式,需要手动管理。
Q2: Stream操作后还能再次使用吗?
A: 不能。Stream只能消费一次,再次使用会抛出IllegalStateException。
Q3: 并行Stream一定比顺序Stream快吗?
A: 不一定。小数据集、简单操作,并行Stream可能更慢。
八、总结
Java Stream API是现代Java开发的必备技能,它让代码更简洁、更易读。掌握Stream API的关键是:
- 理解中间操作和终止操作的区别
- 熟练使用常用操作方法
- 在实际项目中灵活应用
- 注意性能和最佳实践
持续练习和实践,你会发现Stream API的魅力所在!
相关阅读:
评论区