[Java]Stream流
前言
在Java开发日常工作中,集合遍历、数据筛选、字段转换、排序去重、统计分组是高频常见操作。在Java8之前,处理这类业务只能通过for循环搭配if判断完成,不仅代码冗余量大,嵌套层级多,后期维护和阅读都很不方便。
Java8推出的Stream流式编程,结合Lambda表达式,以声明式编程模式处理集合数据。开发者只需定义数据处理规则,无需手动编写循环迭代逻辑,代码简洁紧凑,业务逻辑清晰直观。
本文主要讲解Stream核心概念、常用中间操作和终端操作。每个方法搭配源码参数说明和完整代码案例,适合日常开发直接参考使用。
一、Stream流是什么?
Stream流是Java8新增专门用来处理集合和数组的API,位于java.util.stream包下。它不是集合,不存储数据,只负责数据流水线加工处理,不会修改原集合数据。
简单理解:Stream就是用来替代for循环和if判断,让集合数据处理代码更简洁、更好维护。
1.1 Stream流能干什么?
日常开发集合常见操作:过滤、提取字段、类型转换、排序、去重、分页、统计求和,Stream都封装好了现成方法。使用时直接链式调用,不需要自己写循环遍历。
处理流程:数据源生成流 → 中间加工处理 → 终端操作得到结果。
1.2 Stream核心优势
- 代码量少,十几行for循环逻辑,Stream几行就能写完。
- 可读性强,代码只写“要做什么”,不写“怎么循环”。
- 不修改原数据,处理后返回新集合,数据安全。
- 配合Lambda表达式写法简洁。
1.3 Stream支持的数据源
常用三种:List/Set集合、数组、Stream.of()零散元素。
开发中绝大多数场景都是基于List集合创建流。
二、Stream两大操作类型
中间操作:filter、map、sorted等,只记录规则,不执行,支持链式调用。
终端操作:forEach、count、collect等,触发执行,执行完流就销毁,不能重复使用。
第一部分:Stream中间操作
1、filter 过滤元素
- 源码:
Stream<T> filter(Predicate<? super T> predicate)
-
方法作用:按条件过滤集合元素,符合条件保留,不符合剔除,用于数据筛选。
-
参数讲解:参数是Predicate断言函数。接收每一个元素,返回true或false;true保留元素,false剔除元素。
代码示例:只保留成年人年龄
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
List<Integer> ageList = Arrays.asList(12, 18, 25, 30, 16);
// 过滤18岁及以上
List<Integer> adultList = ageList.stream()
.filter(age -> age >= 18)
.collect(Collectors.toList());
System.out.println(adultList);
}
}
代码说明:集合转流,filter写判断条件,最后collect收集成新List,原集合不会改变。
2、map 映射转换
- 源码:
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
- 方法作用:元素一对一转换、提取字段、数据加工,不删除元素。
- 参数讲解:参数是Function函数,接收旧元素,处理后返回新元素,用于数据类型转换和字段提取。
代码示例:名字全部转大写
List<String> nameList = Arrays.asList("zhangsan", "lisi", "wangwu");
List<String> upperNameList = nameList.stream()
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
System.out.println(upperNameList);
代码说明:map遍历每个元素做统一加工,处理后收集为新集合,适合批量改数据。
3、flatMap 扁平化打散
- 源码:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
- 方法作用:打散嵌套集合、拆分字符串,把多层数据转为单层数据。
- 参数讲解:参数返回Stream子流,底层自动合并所有子流,实现数据扁平化。
代码示例:拆分字符串为单个名字
List<String> strList = Arrays.asList("张三,李四", "王五,赵六");
List<String> allNameList = strList.stream()
.flatMap(str -> Stream.of(str.split(",")))
.collect(Collectors.toList());
System.out.println(allNameList);
代码说明:先拆分字符串,flatMap打散合并为单层普通集合。
4、sorted 排序
- 源码:
Stream<T> sorted(Comparator<? super T> comparator)
- 方法作用:对元素进行升序或降序排序。
- 参数讲解:无参默认升序;传入Comparator自定义排序规则,适合对象排序。
代码示例:数字升序排序
List<Integer> numList = Arrays.asList(5, 2, 9, 1, 3);
List<Integer> sortList = numList.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortList);
5、distinct 去重
- 源码:
Stream<T> distinct()
- 方法作用:去除重复元素,保留唯一数据。
- 参数讲解:无参数,底层靠equals和hashCode判断重复,自定义对象需要重写这两个方法。
List<String> list = Arrays.asList("Java", "Go", "Java", "Python");
List<String> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctList);
6、skip + limit 分页截取
- 源码:
Stream<T> skip(long n)、Stream<T> limit(long maxSize)
- 方法作用:skip跳过前几条,limit限制条数,组合实现简易分页。
- 参数讲解:传入long类型数字,代表跳过个数和截取个数。
List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Integer> pageList = numList.stream()
.skip(3)
.limit(3)
.collect(Collectors.toList());
System.out.println(pageList);
第二部分:Stream终端操作
1、forEach 遍历输出
- 源码:
void forEach(Consumer<? super T> action)
- 方法作用:遍历元素执行操作,无返回值,执行完流关闭。
- 参数讲解:Consumer接收每个元素执行动作,无需返回数据。
List<String> nameList = Arrays.asList("张三","李四","王五");
nameList.stream().forEach(name -> System.out.println(name));
2、count 统计数量
- 源码:
long count()
- 方法作用:统计处理后元素总个数。
- 参数讲解:无参数,返回long类型数量,防止溢出。
List<Integer> ageList = Arrays.asList(12,18,25,30,16);
long count = ageList.stream().filter(age -> age >= 18).count();
System.out.println("成年人数量:" + count);
3、toArray 流转数组
- 源码:
<A> A[] toArray(IntFunction<A[]> generator)
- 方法作用:流转为指定类型数组,适配旧接口。
- 参数讲解:传入数组构造器,直接指定数组类型,无需强转。
List<String> nameList = Arrays.asList("张三","李四");
String[] nameArray = nameList.stream().toArray(String[]::new);
4、allMatch / anyMatch / noneMatch 条件匹配
- 源码:
boolean allMatch(Predicate<? super T> predicate)
- 方法作用:判断集合是否符合指定条件,返回布尔值。
- 参数讲解:Predicate写判断条件。allMatch全部满足true,anyMatch任意一个满足true,noneMatch全部不满足true。
List<Integer> ageList = Arrays.asList(18,20,25);
// 判断是否全部成年
boolean allAdult = ageList.stream().allMatch(age -> age >= 18);
System.out.println(allAdult);
5、findFirst / findAny 查找元素
- 源码:
Optional<T> findFirst()、Optional<T> findAny()
- 方法引用:获取流中单个元素,常用于查询第一条数据。
- 参数讲解:无参数,findFirst取第一个元素,findAny适合并行流取任意一个。
List<Integer> ageList = Arrays.asList(12,18,25);
// 获取第一个成年人
Optional<Integer> firstAge = ageList.stream().filter(age -> age >= 18).findFirst();
System.out.println(firstAge.get());
6、max / min 取最值
- 源码:
Optional<T> max(Comparator<? super T> comparator)
- 方法作用:取最大值、最小值,用于数值统计。
- 参数讲解:传入比较器定义比较规则,获取对应最值。
List<Integer> ageList = Arrays.asList(12,18,25,30);
Optional<Integer> maxAge = ageList.stream().mapToInt(Integer::intValue).max();
System.out.println("最大年龄:" + maxAge.getAsInt());
总结
Stream流核心就分两大块:中间操作和终端操作。
中间操作只是定义数据处理规则,属于延迟执行,不会真正遍历集合;
终端操作才会触发Stream流水线执行计算,执行完流自动关闭,不能重复使用。
日常开发熟练掌握这些方法,完全可以替代传统for循环写法,代码更简洁、维护更轻松。
更多推荐


所有评论(0)