前言

在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核心优势
  1. 代码量少,十几行for循环逻辑,Stream几行就能写完。
  2. 可读性强,代码只写“要做什么”,不写“怎么循环”。
  3. 不修改原数据,处理后返回新集合,数据安全。
  4. 配合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循环写法,代码更简洁、维护更轻松。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐