Java Stream所有方法实例详解
作者:猩火燎猿
该文章详细介绍了Java Stream API的创建、中间操作和终止操作,包括各种流的创建方法、常用中间操作(如filter、map、sorted等)和终止操作(如collect、forEach、reduce等),本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
一、Stream 创建方法
| 方法 | 说明 | 示例 |
|---|---|---|
stream() | 从集合创建顺序流 | list.stream() |
parallelStream() | 创建并行流 | list.parallelStream() |
Stream.of() | 由一组元素创建流 | Stream.of(1, 2, 3) |
Arrays.stream() | 由数组创建流 | Arrays.stream(arr) |
Stream.iterate() | 生成无限流 | Stream.iterate(0, n -> n+2).limit(5) |
Stream.generate() | 生成无限流 | Stream.generate(Math::random).limit(5) |
二、Stream 中间操作(Intermediate Operations)
这些方法返回新的 Stream,允许链式调用。
| 方法 | 说明 | 示例 |
|---|---|---|
filter(Predicate) | 过滤元素 | stream.filter(x -> x > 5) |
map(Function) | 元素映射 | stream.map(x -> x * 2) |
flatMap(Function) | 展开嵌套流 | stream.flatMap(list -> list.stream()) |
distinct() | 去重 | stream.distinct() |
sorted() / sorted(Comparator) | 排序 | stream.sorted() / stream.sorted(Comparator.reverseOrder()) |
peek(Consumer) | 元素遍历但不终止流 | stream.peek(System.out::println) |
limit(long n) | 截取前 n 个元素 | stream.limit(3) |
skip(long n) | 跳过前 n 个元素 | stream.skip(2) |
三、Stream 终止操作(Terminal Operations)
这些方法会产生结果或副作用,流被“消费”后不可再用。
| 方法 | 说明 | 示例 |
|---|---|---|
forEach(Consumer) | 遍历元素 | stream.forEach(System.out::println) |
collect(Collector) | 收集结果 | stream.collect(Collectors.toList()) |
toArray() | 转数组 | stream.toArray() |
reduce(BinaryOperator) | 规约(聚合) | stream.reduce((a, b) -> a + b) |
count() | 计数 | stream.count() |
anyMatch(Predicate) | 是否有任意元素匹配 | stream.anyMatch(x -> x > 5) |
allMatch(Predicate) | 是否所有元素都匹配 | stream.allMatch(x -> x > 5) |
noneMatch(Predicate) | 是否没有元素匹配 | stream.noneMatch(x -> x > 5) |
findFirst() | 查找第一个元素 | stream.findFirst() |
findAny() | 查找任意元素 | stream.findAny() |
min(Comparator) | 最小值 | stream.min(Comparator.naturalOrder()) |
max(Comparator) | 最大值 | stream.max(Comparator.naturalOrder()) |
四、常用 Collector 收集器
| 方法 | 说明 | 示例 |
|---|---|---|
Collectors.toList() | 转 List | stream.collect(Collectors.toList()) |
Collectors.toSet() | 转 Set | stream.collect(Collectors.toSet()) |
Collectors.toMap() | 转 Map | stream.collect(Collectors.toMap(x -> x, x -> x*2)) |
Collectors.joining() | 字符串拼接 | stream.collect(Collectors.joining(",")) |
Collectors.groupingBy() | 分组 | stream.collect(Collectors.groupingBy(x -> x%2)) |
Collectors.partitioningBy() | 分区 | stream.collect(Collectors.partitioningBy(x -> x > 5)) |
Collectors.counting() | 计数 | stream.collect(Collectors.counting()) |
Collectors.summingInt() | 求和 | stream.collect(Collectors.summingInt(x -> x)) |
Collectors.averagingInt() | 平均值 | stream.collect(Collectors.averagingInt(x -> x)) |
Collectors.maxBy() | 最大值 | stream.collect(Collectors.maxBy(Comparator.naturalOrder())) |
Collectors.minBy() | 最小值 | stream.collect(Collectors.minBy(Comparator.naturalOrder())) |
五、示例代码
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤、映射、收集
List<Integer> result = list.stream()
.filter(x -> x > 3)
.map(x -> x * 2)
.collect(Collectors.toList()); // [8, 10, 12]
// 分组
Map<Boolean, List<Integer>> partitioned = list.stream()
.collect(Collectors.partitioningBy(x -> x % 2 == 0));
// 计数
long count = list.stream().filter(x -> x > 3).count();
// 求和
int sum = list.stream().reduce(0, Integer::sum);六、补充说明
- 中间操作是惰性求值,只有终止操作执行时才会遍历数据。
- Stream 不能被复用,终止操作后需重新创建流。
- 并行流(parallelStream)适用于大数据量,但需注意线程安全和性能。
七、Stream 高级用法
1. 多级分组
Map<String, Map<Integer, List<User>>> multiGroup = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.groupingBy(User::getAge)
));说明:先按城市分组,再按年龄分组,结果是嵌套的 Map。
2. 分区与分组的区别
- 分区:只有两组(true/false),适合布尔条件。
- 分组:可以有多个分组键。
Map<Boolean, List<Integer>> partition = list.stream()
.collect(Collectors.partitioningBy(x -> x > 3));3. 自定义收集器
可以通过Collector.of自定义收集逻辑:
Collector<Integer, ?, Set<Integer>> toCustomSet = Collector.of(
HashSet::new,
Set::add,
(left, right) -> { left.addAll(right); return left; }
);
Set<Integer> set = list.stream().collect(toCustomSet);4. 并行流
list.parallelStream()
.filter(x -> x > 3)
.forEach(System.out::println);注意:并行流适合 CPU 密集型任务,IO 密集型需谨慎;操作需线程安全。
5. Optional 结合 Stream
Optional<Integer> first = list.stream().filter(x -> x > 3).findFirst(); first.ifPresent(System.out::println);
八、常见陷阱与注意点
- 流只能用一次
- 流操作后已关闭,不能再操作,否则抛异常。
- 修改集合元素
- 不建议在流操作中直接修改原集合元素,推荐返回新集合。
- 空指针问题
- Stream 操作前应确保集合非 null,推荐使用
Optional.ofNullable(list).orElse(Collections.emptyList()).stream()。
- Stream 操作前应确保集合非 null,推荐使用
- 性能问题
distinct()、sorted()等操作会增加性能消耗。- 并行流适合数据量大且操作无副作用的场景。
九、部分方法源码简析
1. filter
default Stream<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
return new ReferencePipeline.StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED) {
@Override
Sink<T> opWrapSink(int flags, Sink<T> sink) {
return new Sink.ChainedReference<T, T>(sink) {
@Override
public void accept(T t) {
if (predicate.test(t)) downstream.accept(t);
}
};
}
};
}说明:filter 实际上是对每个元素执行 Predicate 判断,符合条件则传递到下游。
2. map
default <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
Objects.requireNonNull(mapper);
return new ReferencePipeline.StatelessOp<>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED) {
@Override
Sink<T> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<T, R>(sink) {
@Override
public void accept(T t) {
downstream.accept(mapper.apply(t));
}
};
}
};
}说明:map 通过 Function 映射每个元素,返回新的流。
十、典型业务场景举例
1. 数据去重并排序
List<String> sorted = list.stream()
.distinct()
.sorted()
.collect(Collectors.toList());2. 统计分组后的最大值
Map<String, Optional<User>> maxAgeByCity = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.maxBy(Comparator.comparingInt(User::getAge))
));3. 多字段分组统计
Map<String, Map<Integer, Long>> groupCount = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.groupingBy(User::getAge, Collectors.counting())
));4. 批量字段提取
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());5. Stream 处理 Map
Map<String, Integer> map = ...;
List<String> keys = map.entrySet().stream()
.filter(e -> e.getValue() > 5)
.map(Map.Entry::getKey)
.collect(Collectors.toList());十一、Stream API 性能建议
- 尽量减少中间操作链长度,必要时合并操作。
- 大数据量推荐并行流,但需测试线程安全和实际效率。
- 优先使用基本类型流(IntStream、LongStream、DoubleStream)可减少装箱/拆箱开销。
- 避免在流中写复杂逻辑,建议拆分为多个方法提高可读性。
十二、Stream 相关扩展
- IntStream/LongStream/DoubleStream:针对基本类型的流,效率更高。
- Collectors.toConcurrentMap:并发收集器,适合并行流。
- Collectors.mapping:在分组或分区时对元素做映射。
- Collectors.reducing:自定义规约操作。
十三、基本类型流详解
Java 提供了三种基本类型流,分别是 IntStream、LongStream 和 DoubleStream,它们避免了自动装箱/拆箱,提高了性能。
创建方式
IntStream intStream = IntStream.of(1, 2, 3, 4); LongStream longStream = LongStream.range(1, 10); // [1,9] DoubleStream doubleStream = DoubleStream.generate(Math::random).limit(5);
常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
sum() | 求和 | intStream.sum() |
average() | 平均值 | intStream.average().getAsDouble() |
max() / min() | 最大/最小值 | intStream.max().getAsInt() |
boxed() | 转换为对象流 | intStream.boxed() |
mapToObj() | 基本类型流转对象流 | intStream.mapToObj(String::valueOf) |
十四、流的收集与转换技巧
1. 转换为 Map,处理 key 重复
// value 相加
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(
User::getName,
User::getScore,
Integer::sum // 合并函数
));2. 收集为不可变集合
List<String> unmodifiableList = list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
));3. 多字段分组
Map<String, Map<Integer, List<User>>> group = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.groupingBy(User::getAge)
));十五、Stream 与并发/线程安全
- 并行流:
parallelStream()会自动分片并行处理,适合无状态、无副作用的操作。 - 线程安全收集:使用
Collectors.toConcurrentMap、ConcurrentHashMap等。
ConcurrentMap<String, Integer> concurrentMap = list.parallelStream()
.collect(Collectors.toConcurrentMap(User::getName, User::getScore));十六、Stream 排序技巧
1. 单字段排序
list.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());2. 多字段排序
list.stream()
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());3. 逆序排序
list.stream()
.sorted(Comparator.comparing(User::getAge).reversed())
.collect(Collectors.toList());十七、Stream 实用代码片段
1. 找出重复元素
Set<Integer> seen = new HashSet<>();
Set<Integer> duplicates = list.stream()
.filter(n -> !seen.add(n))
.collect(Collectors.toSet());2. 分页功能
int page = 2, size = 5;
List<Integer> pageList = list.stream()
.skip((page - 1) * size)
.limit(size)
.collect(Collectors.toList());3. 按条件统计数量
long count = list.stream().filter(x -> x > 10).count();
4. 合并两个 List 并去重
List<Integer> merged = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());十八、常见问题与解决方案
1. Stream 只用一次
Stream<String> s = list.stream(); s.forEach(System.out::println); // s.forEach(...) // 抛异常,不可再用
解决:重新创建流。
2. NullPointerException
集合为 null 时不能直接调用 stream()。
List<String> safeList = Optional.ofNullable(list)
.orElse(Collections.emptyList());
safeList.stream()...3. 性能瓶颈
- 避免在流操作中写复杂逻辑,拆分方法。
- 并行流适合数据量大且操作无副作用,需实际测试。
十九、Collectors 的扩展用法
1. mapping
分组后对分组内元素做映射:
Map<String, List<String>> nameGroup = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.mapping(User::getName, Collectors.toList())
));2. reducing
自定义聚合:
int totalScore = users.stream()
.collect(Collectors.reducing(0, User::getScore, Integer::sum));二十、Stream 处理嵌套集合
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1,2),
Arrays.asList(3,4)
);
List<Integer> flat = nested.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());二十一、Stream API 新增方法(Java 9+)
如果你用的是 Java 9 及以上,还可以用:
takeWhile(Predicate):从头开始,遇到不满足条件就停止。dropWhile(Predicate):从头开始,遇到不满足条件后开始收集。Stream.ofNullable(obj):对象为 null 返回空流,否则返回单元素流。
Stream.ofNullable(null).count(); // 0
Stream.ofNullable("abc").count(); // 1二十二、Stream 与 Lambda 表达式最佳实践
- 保持流操作简洁,避免嵌套 lambda 过多。
- 推荐将复杂逻辑提取为方法引用或单独方法。
- 用注释说明复杂的流链。
总结
Java Stream 提供了丰富的 API,适合处理集合的各种操作,包括过滤、映射、分组、聚合、排序、去重等。掌握 Stream 可以极大提升 Java 代码的简洁性和表现力。
到此这篇关于Java Stream所有方法实例详解的文章就介绍到这了,更多相关Java Stream所有方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
