本文详细介绍了 Java Stream 流的重要知识点。包括数据源与操作分离(不存储数据,不可复用)、惰性求值与短路优化;以及流的创建方式,如集合创建、数组 / 值创建、文件创建;然后介绍中间操作,像过滤与切片等;还涉及终止操作、集合归约与 Collectors 工具类、并行流与线程安全、性能优化与日常工作中使用Java Stream的注意点等等。
本文目录
- 一、Stream核心概念
- 1. 数据源与操作分离
- 2. 惰性求值与短路优化
- 二、流的创建方式
- 1. 集合创建
- 2. 数组/值创建
- 3. 文件
- 三、中间操作
- 1. 过滤与切片
- 2. 映射
- 3. 排序
- 4. 观察中间结果
- 四、终止操作
- 1. 匹配与查找
- 2. 归约与统计
- 3. 遍历与消费
- 五、集合归约与Collectors工具类
- 1. 常用收集器
- 2. 分组与分区
- 3. 统计与连接
- 六、并行流与线程安全
- 并行流使用
- 七、日常使用注意点
- 1. 避免重复创建流
- 2. 优先选择基本类型流
- 3. 短路操作优化
- 八、日常工作实战案例
- 1. 订单金额统计
- 2. 商品筛选与排序
- 3. 用户行为分析
一、Stream核心概念
1. 数据源与操作分离
Stream不存储数据,仅对数据源(如集合、数组、I/O)进行计算。需要注意的是,流是不可复用的,一旦流被消费,就不能再次使用,否则会抛出IllegalStateException
异常。
2. 惰性求值与短路优化
- 中间操作(如
filter
、map
)是延迟执行的,只有当遇到终止操作(如collect
)时才会触发计算。 - 短路操作(如
findFirst
、anyMatch
)可以提前终止遍历,减少不必要的计算。
二、流的创建方式
1. 集合创建
List<String> list = Arrays.asList("a", "b");
Stream<String> stream = list.stream(); // 顺序流
Stream<String> parallelStream = list.parallelStream(); // 并行流
2. 数组/值创建
Stream<String> stream1 = Stream.of("a", "b");
Stream<String> stream2 = Arrays.stream(new String[]{"a", "b"});
3. 文件
Stream<String> lines = Files.lines(Paths.get("data.txt")); // 读取文件
三、中间操作
1. 过滤与切片
filter(Predicate)
:用于过滤元素。distinct()
:去除重复元素。limit(n)
:截断前n个元素。skip(n)
:跳过前n个元素。
2. 映射
map(Function)
:一对一转换,提取对象的某个字段。flatMap(Function)
:扁平化操作,可将List<List<T>>
转为List<T>
。
3. 排序
sorted()
:自然排序。sorted(Comparator)
:自定义排序。
4. 观察中间结果
.peek(e -> System.out.println("Processing: " + e))
四、终止操作
1. 匹配与查找
anyMatch(Predicate)
:判断是否有任一元素满足条件。allMatch(Predicate)
:判断是否所有元素都满足条件。findFirst()
:返回第一个元素(返回类型为Optional
)。findAny()
:返回任意元素,在并行流中更高效。
2. 归约与统计
reduce(BinaryOperator)
:用于聚合操作,如求和。collect(Collectors)
:将流转换为集合(如List、Map)。count()
:统计元素总数。max(Comparator)
/min(Comparator)
:找出元素的极值。
3. 遍历与消费
forEach(Consumer)
:遍历元素,无顺序保证。forEachOrdered(Consumer)
:按顺序遍历元素。
五、集合归约与Collectors工具类
1. 常用收集器
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
Map<String, User> map = stream.collect(Collectors.toMap(User::getId, Function.identity()));
2. 分组与分区
// 按年龄分组
Map<Integer, List<User>> ageGroup = users.stream().collect(Collectors.groupingBy(User::getAge));// 跟据年龄是否>=18进行分区
Map<Boolean, List<User>> partition = users.stream().collect(Collectors.partitioningBy(u -> u.getAge() >= 18));
3. 统计与连接
Double average = users.stream().collect(Collectors.averagingInt(User::getAge)); // 平均年龄String names = users.stream().map(User::getName).collect(Collectors.joining(", ")); // 拼接字符串
六、并行流与线程安全
并行流使用
List<Integer> result = list.parallelStream().filter(n -> n % 2 == 0).collect(Collectors.toList());
七、日常使用注意点
1. 避免重复创建流
错误示例:
for (int i = 0; i < 10; i++) {list.stream().filter(...); // 多次创建流
}
2. 优先选择基本类型流
IntStream
、LongStream
、DoubleStream
可以避免装箱开销:
IntStream.range(0, 100).sum(); // 比Stream<Integer>高效
3. 短路操作优化
尽早使用limit
、findFirst
等操作可以减少计算量:
users.stream().filter(u -> u.getAge() > 30).findFirst() // 找到第一个符合条件后即终止.orElse(null);
八、日常工作实战案例
1. 订单金额统计
// 计算所有订单总金额
double totalAmount = orders.stream().mapToDouble(Order::getAmount).sum();// 按用户分组统计消费总额
Map<Long, Double> userTotal = orders.stream().collect(Collectors.groupingBy(Order::getUserId,Collectors.summingDouble(Order::getAmount)));
2. 商品筛选与排序
// 筛选库存>0的商品并按价格排序
List<Product> availableProducts = products.stream().filter(p -> p.getStock() > 0).sorted(Comparator.comparing(Product::getPrice)).collect(Collectors.toList());
3. 用户行为分析
// 统计最近一周活跃用户数
long activeUsers = userLogs.stream().filter(log -> log.getAction().equals("LOGIN")).filter(log -> log.getTime().isAfter(LocalDateTime.now().minusDays(7))).map(UserLog::getUserId).distinct().count();
在日常开发中,合理运用Stream流可以让代码更加简洁、高效,提高开发效率。希望本文对你有所帮助!
← 上一篇 Java进阶——数据类型深入解析 | 记得点赞、关注、收藏哦! | 下一篇 Java进阶——注解一文全懂 → |