一、Stream API 核心概念
1. Stream 的特性
java
复制
下载
// Stream 的本质:数据流的高级抽象
// 特点:不存储数据、惰性求值、可消费一次、支持并行
// 创建Stream的几种方式
Stream<String> stream1 = list.stream(); // 集合
Stream<String> stream2 = Stream.of("a", "b", "c"); // 值
Stream<String> stream3 = Arrays.stream(array); // 数组
Stream<Integer> stream4 = Stream.iterate(0, n -> n + 1); // 无限流
Stream<Double> stream5 = Stream.generate(Math::random); // 生成流
IntStream intStream = IntStream.range(1, 100); // 数值流
二、常用操作分类
1. 中间操作(Intermediate Operations)
🔧 筛选与切片
java
复制
下载
// filter - 过滤
List<User> adults = users.stream()
.filter(user -> user.getAge() >= 18)
.collect(Collectors.toList());
// distinct - 去重(基于equals和hashCode)
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// limit - 限制数量
List<String> first10 = list.stream()
.limit(10)
.collect(Collectors.toList());
// skip - 跳过元素
List<String> after5th = list.stream()
.skip(5)
.collect(Collectors.toList());
// takeWhile - Java 9+ (满足条件时取元素)
List<Integer> lessThan5 = numbers.stream()
.takeWhile(n -> n < 5)
.collect(Collectors.toList());
// dropWhile - Java 9+ (满足条件时丢弃元素)
List<Integer> afterNegative = numbers.stream()
.dropWhile(n -> n < 0)
.collect(Collectors.toList());
🔄 映射操作
java
复制
下载
// map - 一对一转换
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// flatMap - 一对多展开
List<String> allTags = articles.stream()
.flatMap(article -> article.getTags().stream())
.distinct()
.collect(Collectors.toList());
// mapToInt/mapToLong/mapToDouble - 转换为数值流
int totalAge = users.stream()
.mapToInt(User::getAge)
.sum();
📊 排序操作
java
复制
下载
// sorted - 自然排序
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// sorted(Comparator) - 自定义排序
List<User> sortedUsers = users.stream()
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());
// 复杂排序示例
List<Employee> employees = empList.stream()
.sorted(Comparator.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary, Comparator.reverseOrder())
.thenComparing(Employee::getName))
.collect(Collectors.toList());
2. 终端操作(Terminal Operations)
🔍 匹配与查找
java
复制
下载
// allMatch - 所有元素匹配
boolean allAdults = users.stream()
.allMatch(user -> user.getAge() >= 18);
// anyMatch - 任一元素匹配
boolean hasAdmin = users.stream()
.anyMatch(user -> user.getRole().equals("ADMIN"));
// noneMatch - 没有元素匹配
boolean noMinors = users.stream()
.noneMatch(user -> user.getAge() < 18);
// findFirst - 返回第一个元素
Optional<User> firstUser = users.stream()
.findFirst();
// findAny - 返回任意元素(并行流中更高效)
Optional<User> anyUser = users.parallelStream()
.findAny();
// count - 计数
long adultCount = users.stream()
.filter(user -> user.getAge() >= 18)
.count();
// max/min - 最大/最小值
Optional<User> oldestUser = users.stream()
.max(Comparator.comparing(User::getAge));
Optional<Integer> minValue = numbers.stream()
.min(Integer::compareTo);
📦 归约与收集
java
复制
下载
// reduce - 归约操作
// 形式1:BinaryOperator
Optional<Integer> sum1 = numbers.stream()
.reduce(Integer::sum);
// 形式2:初始值 + BinaryOperator
Integer sum2 = numbers.stream()
.reduce(0, Integer::sum);
// 形式3:复杂归约(适用于并行流)
Integer sum3 = numbers.parallelStream()
.reduce(0, Integer::sum, Integer::sum);
// collect - 收集器(最强大的终端操作)
// 1. 转换为集合
List<String> nameList = users.stream()
.map(User::getName)
.collect(Collectors.toList());
Set<String> nameSet = users.stream()
.map(User::getName)
.collect(Collectors.toSet());
// 2. 转换为Map
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 处理重复key
Map<String, User> userByName = users.stream()
.collect(Collectors.toMap(
User::getName,
Function.identity(),
(existing, replacement) -> existing // 合并函数
));
// 3. 分组
Map<String, List<User>> usersByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
// 多级分组
Map<String, Map<String, List<User>>> usersByCityAndGender = users.stream()
.collect(Collectors.groupingBy(User::getCity,
Collectors.groupingBy(User::getGender)));
// 4. 分区(分为true/false两组)
Map<Boolean, List<User>> partitioned = users.stream()
.collect(Collectors.partitioningBy(user -> user.getAge() >= 18));
// 5. 连接字符串
String joinedNames = users.stream()
.map(User::getName)
.collect(Collectors.joining(", ", "[", "]"));
// 6. 统计汇总
IntSummaryStatistics stats = users.stream()
.collect(Collectors.summarizingInt(User::getAge));
// 包含:count, sum, min, max, average
// 7. 自定义收集器
List<User> customList = users.stream()
.collect(ArrayList::new, List::add, List::addAll);
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
三、高级操作与模式
1. 无限流处理
java
复制
下载
// 生成斐波那契数列
Stream.iterate(new long[]{0, 1}, t -> new long[]{t[1], t[0] + t[1]})
.limit(20)
.map(t -> t[0])
.forEach(System.out::println);
// 生成随机数流
Stream.generate(Math::random)
.limit(10)
.forEach(System.out::println);
2. 并行流处理
java
复制
下载
// 顺序流 vs 并行流
List<Integer> numbers = IntStream.range(1, 1000000)
.boxed()
.collect(Collectors.toList());
// 顺序处理
long start1 = System.currentTimeMillis();
long sum1 = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
long time1 = System.currentTimeMillis() - start1;
// 并行处理
long start2 = System.currentTimeMillis();
long sum2 = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
long time2 = System.currentTimeMillis() - start2;
3. 流操作链优化
java
复制
下载
// 优化前 - 性能较差
List<String> result = users.stream()
.filter(user -> user.getAge() > 18)
.map(User::getName)
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// 优化后 - 调整操作顺序
List<String> optimized = users.stream()
.filter(user -> user.getAge() > 18)
.map(User::getName)
.filter(name -> name.startsWith("A"))
.sorted() // 尽早排序可以减少后续操作的数据量
.map(String::toUpperCase)
.collect(Collectors.toList());
四、性能考量与最佳实践
1. 性能对比基准测试
java
复制
下载
// 测试不同操作的性能影响
public class StreamPerformance {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void testForLoop() {
long sum = 0;
for (int i = 0; i < 1_000_000; i++) {
if (i % 2 == 0) {
sum += i * 2;
}
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void testStream() {
long sum = IntStream.range(0, 1_000_000)
.filter(i -> i % 2 == 0)
.map(i -> i * 2)
.sum();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void testParallelStream() {
long sum = IntStream.range(0, 1_000_000)
.parallel()
.filter(i -> i % 2 == 0)
.map(i -> i * 2)
.sum();
}
}
2. 性能影响因素分析
⚡ 影响性能的关键因素:
-
数据量大小
-
小数据量(<1000):传统循环可能更快
-
大数据量:Stream优势明显
-
-
操作类型
-
低开销操作(map、filter):Stream性能好
-
高开销操作(sorted、distinct):需要评估
-
-
并行流适用性
java
复制
下载
// 适合并行的场景: // 1. 数据量大(>10000) // 2. 计算密集型操作 // 3. 操作之间无状态依赖 // 不适合并行的场景: // 1. 数据量小 // 2. IO密集型操作 // 3. 操作顺序敏感 // 4. 共享可变状态
3. 性能优化策略
🎯 操作顺序优化
java
复制
下载
// 原则:尽早过滤,推迟排序
// 坏例子
List<String> bad = list.stream()
.sorted() // 过早排序
.filter(s -> s.length() > 3) // 排序了很多不需要的元素
.map(String::toUpperCase)
.collect(Collectors.toList());
// 好例子
List<String> good = list.stream()
.filter(s -> s.length() > 3) // 先过滤,减少数据量
.sorted() // 只对需要的数据排序
.map(String::toUpperCase)
.collect(Collectors.toList());
🔄 避免装箱开销
java
复制
下载
// 使用原始类型特化流
// 错误:有装箱开销
List<Integer> list = new ArrayList<>();
int sum = list.stream()
.mapToInt(Integer::intValue) // 额外拆箱
.sum();
// 正确:直接使用IntStream
IntStream intStream = IntStream.range(0, 1000000);
int sum = intStream.sum(); // 无装箱开销
// 性能对比(使用JMH测试):
// IntStream.sum(): 0.234 ms/op
// Stream<Integer> + mapToInt: 0.567 ms/op
// Stream<Integer> + reduce: 0.891 ms/op
📊 并行流调优
java
复制
下载
// 自定义ForkJoinPool
ForkJoinPool customPool = new ForkJoinPool(4); // 设置并行度
long result = customPool.submit(() ->
list.parallelStream()
.mapToInt(Integer::intValue)
.sum()
).get();
// 并行流最佳实践:
// 1. 设置合适的并行度
// 2. 避免共享可变状态
// 3. 使用无状态操作
// 4. 考虑负载均衡
4. 内存使用考量
java
复制
下载
// Stream vs 传统集合的内存使用
public class MemoryUsage {
// Stream处理大文件的优势
public void processLargeFile() throws IOException {
// 传统方式:读取整个文件到内存
List<String> lines = Files.readAllLines(Paths.get("large.txt"));
// 内存占用高
// Stream方式:流式处理
try (Stream<String> stream = Files.lines(Paths.get("large.txt"))) {
long count = stream.filter(line -> line.contains("error"))
.count();
}
// 内存占用低
}
// 避免中间集合的创建
public void avoidIntermediateCollections() {
// 不好:创建了中间集合
List<String> intermediate = users.stream()
.map(User::getName)
.collect(Collectors.toList()); // 中间集合
List<String> result = intermediate.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// 好:直接链式操作
List<String> optimized = users.stream()
.map(User::getName)
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
}
}
五、常见陷阱与解决方案
1. 流只能消费一次
java
复制
下载
// 错误示例
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // IllegalStateException
// 解决方案:每次需要时重新创建流
Supplier<Stream<String>> streamSupplier = () -> list.stream();
streamSupplier.get().forEach(System.out::println);
streamSupplier.get().forEach(System.out::println); // 正常
2. 并行流的线程安全问题
java
复制
下载
// 错误:并行修改共享状态
List<Integer> list = new ArrayList<>();
IntStream.range(0, 10000).parallel()
.forEach(i -> list.add(i)); // ConcurrentModificationException
// 正确1:使用线程安全集合
List<Integer> safeList = Collections.synchronizedList(new ArrayList<>());
IntStream.range(0, 10000).parallel()
.forEach(i -> safeList.add(i));
// 正确2:使用collect方法
List<Integer> collected = IntStream.range(0, 10000).parallel()
.boxed()
.collect(Collectors.toList());
3. 空指针异常处理
java
复制
下载
// 使用Optional避免NPE
Optional<String> result = users.stream()
.map(User::getAddress)
.filter(Objects::nonNull)
.map(Address::getCity)
.filter(city -> city.startsWith("A"))
.findFirst();
// 使用flatMap处理嵌套Optional
Optional<String> nested = users.stream()
.map(User::getAddress)
.flatMap(Optional::stream) // Java 9+
.map(Address::getCity)
.findFirst();
六、实战应用场景
1. 数据库查询结果处理
java
复制
下载
// 模拟从数据库获取的数据
List<Order> orders = orderRepository.findAll();
// 复杂的数据处理
Map<Long, Double> userTotalSpent = orders.stream()
.filter(order -> order.getStatus() == OrderStatus.COMPLETED)
.filter(order -> order.getCreateTime().isAfter(LocalDate.now().minusMonths(1)))
.collect(Collectors.groupingBy(
Order::getUserId,
Collectors.summingDouble(Order::getTotalAmount)
));
// 找出消费最高的用户
Optional<Map.Entry<Long, Double>> topSpender = userTotalSpent.entrySet().stream()
.max(Map.Entry.comparingByValue());
2. 批量数据处理
java
复制
下载
// 批量更新用户状态
List<User> updatedUsers = users.stream()
.peek(user -> {
if (user.isInactive()) {
user.setStatus(UserStatus.INACTIVE);
}
})
.collect(Collectors.toList());
// 分批处理(每100条一批)
int batchSize = 100;
List<List<User>> batches = IntStream.range(0, users.size())
.boxed()
.collect(Collectors.groupingBy(index -> index / batchSize))
.values().stream()
.map(indices -> indices.stream()
.map(users::get)
.collect(Collectors.toList()))
.collect(Collectors.toList());
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
七、性能测试数据参考
| 操作类型 | 数据量 | 传统循环 | Stream | 并行Stream |
|---|---|---|---|---|
| 简单过滤 | 100万 | 15ms | 18ms | 5ms |
| 复杂映射 | 100万 | 45ms | 52ms | 12ms |
| 排序 | 10万 | 25ms | 28ms | 8ms |
| 分组 | 10万 | 32ms | 35ms | 9ms |
| 归约求和 | 100万 | 8ms | 10ms | 3ms |
测试环境:JDK 17, Intel i7-10700, 16GB RAM
八、最佳实践总结
✅ 使用Stream的场景:
-
数据处理流水线:多个转换操作链式调用
-
集合复杂操作:分组、分区、统计等
-
并行处理:大数据量计算密集型任务
-
函数式风格:代码更声明式,易读易维护
❌ 避免使用Stream的场景:
-
简单迭代:只有一个简单操作
-
性能关键路径:需要极致性能的场景
-
异常处理复杂:需要精细的异常控制
-
状态依赖操作:操作间有复杂的依赖关系
⚡ 性能黄金法则:
-
数据量决定并行度:小数据用串行,大数据考虑并行
-
操作顺序优化:filter在前,sorted在后
-
避免装箱拆箱:使用原始类型特化流
-
合理使用短路操作:findFirst、limit等
-
监控内存使用:避免创建不必要的中间集合
记住 :Stream API的价值不仅在于性能,更在于代码的表达力 和可维护性。在大多数业务场景下,Stream带来的代码简洁性和可读性提升,远比微小的性能差异更重要。