JavaSE加强:Stream流

Stream 流是 Java8 新增,用于集合 / 数组的函数式编程,简化遍历、筛选、转换、聚合代码,告别繁琐 for 循环,链式调用。

包:java.util.stream.*

一、Stream 流定义与核心概念

1. 什么是 Stream 流

Stream(流)是数据处理流水线 ,不存储数据,数据源来自集合、数组等。它可以对数据源做一系列中间操作,最后通过终端操作得到结果。

类比工厂流水线:原材料(集合)→过滤、加工(中间操作)→产出成品(终端操作)

2. Stream 五大核心特性

  1. 无存储:Stream 本身不保存元素,元素保存在原始数据源。流只是操作规则。
  2. 惰性求值(延迟执行) :中间操作不会立刻执行,只有调用终端操作时,所有中间操作才一次性执行。
  3. 不修改源数据:所有操作不会改动原始集合 / 数组,返回新流或新结果。
  4. 一次性消费 :流一旦执行终端操作,流就关闭销毁,不能重复使用,重复使用抛出IllegalStateException。
  5. 支持串行 / 并行 :stream()串行单线程;parallelStream()自动多线程并行处理大数据。

3. Stream 使用三步固定流程

① 获取流 :拿到数据源对应的 Stream 对象 ② 中间操作 :链式调用,对数据做多次处理(返回新 Stream) ③ 终端操作:触发流水线执行,生成结果,流关闭

必须:中间操作可以多个,终端操作只能一个,且写在最后

二、Stream 流的获取方式(4 大类)

java 复制代码
import java.util.*;
import java.util.stream.Stream;

public class GetStreamDemo {
    public static void main(String[] args) {
        //1. Collection单列集合获取流:集合.stream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        Stream<String> parallelStream = list.parallelStream(); //并行流

        //2. Map双列集合获取流(Map没有直接stream(),要转单列)
        Map<String,Integer> map = new HashMap<>();
        Stream<String> keyStream = map.keySet().stream(); //键集合流
        Stream<Integer> valueStream = map.values().stream(); //值集合流
        Stream<Map.Entry<String,Integer>> entryStream = map.entrySet().stream(); //键值对流

        //3. 数组获取流:Arrays.stream(数组)
        String[] arr = {"A","B","C"};
        Stream<String> stream3 = Arrays.stream(arr);

        //4. 多个零散数据直接创建流:Stream.of(T... values)
        Stream<String> stream4 = Stream.of("张三","李四","王五");
    }
}

⚠️注意:Stream.of()底层是数组,不能传 null 数组,会空指针。

三、Stream 操作分类(重点)

分为中间操作(Intermediate) 和终端操作(Terminal)

表格

分类 特点 常用 API 返回值
中间操作 链式调用、惰性执行,返回新 Stream filter、map、flatMap、sorted、distinct、limit、skip、peek Stream<T>
终端操作 触发流水线,执行完毕流关闭,不再返回 Stream forEach、collect、count、max、min、reduce、anyMatch、allMatch、noneMatch void / 普通值(Long、Optional、集合等)

一句话区分:返回 Stream 就是中间操作;返回其他 /void 就是终端操作

四、常用中间操作详解 + 代码示例

1. filter 过滤(Predicate 接口)

作用:根据条件筛选元素,保留返回 true 的元素

java 复制代码
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
//保留大于3的数字
List<Integer> res = list.stream()
        .filter(num -> num >3)
        .collect(Collectors.toList());
System.out.println(res); // [4,5,6]

2. map 映射转换(Function 接口)

作用:对流中每一个元素做类型 / 内容转换,一个元素映射成一个新元素

java 复制代码
List<String> nameList = Arrays.asList("张三","李四","王五");
//把每个名字转为名字长度
List<Integer> lenList = nameList.stream()
        .map(String::length)
        .collect(Collectors.toList());
System.out.println(lenList); // [2,2,2]

对象场景:map(Movie::getActor) 提取电影对象的演员属性,就是你前面代码用到的!

3. flatMap 扁平化映射

作用:把每个元素转成流,然后把所有流合并成一个大流。用于拆分集合里面嵌套集合。

java 复制代码
List<List<String>> nestedList = Arrays.asList(
        Arrays.asList("a","b"),
        Arrays.asList("c","d")
);
//扁平化,把两层集合拆成单层流
List<String> flat = nestedList.stream()
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
System.out.println(flat); // [a,b,c,d]

4. sorted 排序

  • sorted():自然排序,元素类需要实现 Comparable 接口
  • sorted(Comparator):自定义比较器排序(推荐)
java 复制代码
List<Integer> nums = Arrays.asList(5,1,3,9,2);
//升序
List<Integer> sortAsc = nums.stream().sorted().collect(Collectors.toList());
//降序
List<Integer> sortDesc = nums.stream().sorted((o1,o2)->o2-o1).collect(Collectors.toList());
System.out.println(sortAsc); // [1,2,3,5,9]

5. distinct 去重

底层依靠equals()和hashCode()方法

java 复制代码
List<Integer> numList = Arrays.asList(1,2,2,3,3,3);
List<Integer> distinctList = numList.stream()
        .distinct()
        .collect(Collectors.toList());
System.out.println(distinctList); // [1,2,3]

(tips)自定义对象去重

必须重写 equals 和 hashCode

对象去重要在实体类内部重写equals和hashcode方法(Stream底层去重会调用linkHashSet,会先比较哈希值,如果相同则会调用object原生equals默认比较存储地址)

  • hashcode:由地址获取哈希值转变为属性获取
  • equals:直接比较存储地址变为先比较地址,然后保证参数对象不为空且属于实体类,最后返回属性比较的布尔值
java 复制代码
public class Student {
    private Integer id;
    private String name;

    // 构造、get、set、toString
    
    // ✅重写equals:按业务属性判断对象内容是否相等
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        // 业务:id和name相同,就认为是同一个学生
        return Objects.equals(id, student.id) && Objects.equals(name, student.name);
    }

    // ✅重写hashCode:和equals使用完全相同的属性生成哈希码
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

6. limit (long maxSize):截取前 N 个元素

java 复制代码
List<Integer> list = Arrays.asList(10,20,30,40,50);
List<Integer> limitList = list.stream()
        .limit(3)
        .collect(Collectors.toList());
System.out.println(limitList); // [10,20,30]

7. skip (long n):跳过前 N 个元素

java 复制代码
List<Integer> list = Arrays.asList(10,20,30,40,50);
List<Integer> skipList = list.stream()
        .skip(2)
        .collect(Collectors.toList());
System.out.println(skipList); // [30,40,50]

limit+skip 组合可以实现分页:skip (页码 - 1 * 每页条数).limit (每页条数)

8. peek 调试用,遍历元素,不修改流

多用于打印看中间数据,开发调试。

java 复制代码
List<Integer> res = Stream.of(1,2,3)
        .peek(num-> System.out.println("原始:"+num))
        .map(n->n*2)
        .collect(Collectors.toList());

五、常用终端操作详解 + 代码示例

1. forEach (Consumer):遍历流中元素

java 复制代码
Stream.of("A","B","C").forEach(System.out::println);

❗注意:forEach 是终端操作,执行完流就关闭,不能继续链式调用。

2. collect (Collectors):收集,最常用!

把流的数据收集回集合(List/Set/Map),需要导入java.util.stream.Collectors

java 复制代码
List<String> list = Stream.of("a","b","c").collect(Collectors.toList());
Set<String> set = Stream.of("a","b","c").collect(Collectors.toSet());
// 收集到Map:参数1-key,参数2-value,参数3解决key重复冲突
Map<String,Integer> map = Stream.of("张三","李四")
        .collect(Collectors.toMap(s->s, s->s.length(),(oldVal,newVal)->oldVal));
  • Collectors.joining():字符串拼接
java 复制代码
String str = Stream.of("张三","李四","王五").collect(Collectors.joining(","));
System.out.println(str); //张三,李四,王五

3. count ():统计元素个数,返回 long

java 复制代码
long count = Stream.of(1,2,3,4).filter(n->n>2).count();
System.out.println(count); //2

4. max () /min ():获取最大 / 最小值,返回 Optional

java 复制代码
Optional<Integer> max = Stream.of(5,2,9,1).max(Integer::compareTo);
Optional<Integer> min = Stream.of(5,2,9,1).min(Integer::compareTo);
System.out.println(max.get()); //9

5. reduce 归约、聚合,把流元素反复合并成一个结果

求和、求乘积

java 复制代码
// 参数1:初始值;参数2:累加器
int sum = Stream.of(1,2,3,4).reduce(0,Integer::sum);
System.out.println(sum); //10

6. 匹配操作(返回 boolean)

  • allMatch():全部元素满足条件返回 true
  • anyMatch():任意一个满足返回 true
  • noneMatch():全部都不满足返回 true
java 复制代码
List<Integer> num = Arrays.asList(2,4,6);
boolean allEven = num.stream().allMatch(n->n%2==0); //true
boolean has5 = num.stream().anyMatch(n->n==5); //false

六、原始类型流(IntStream、LongStream、DoubleStream)

普通Stream<Integer>会有自动装箱拆箱损耗,大数据推荐使用原始流。 mapToInt / mapToLong / mapToDouble 转为原始流

java 复制代码
List<Integer> list = Arrays.asList(1,2,3,4);
int sum = list.stream().mapToInt(Integer::intValue).sum();
System.out.println(sum);

七、综合实战案例(电影对象)

场景:Movie 类,name 电影名,actor 演员名字;筛选出演员包含 star 的电影,收集为 List

java 复制代码
class Movie{
    private String name;
    private String actor;
    //构造器 get set toString
    public String getActor() {return actor;}
}

public class MovieStreamDemo {
    public static void main(String[] args) {
        List<Movie> movies = new ArrayList<>();
        movies.add(new Movie("电影1","张三"));
        movies.add(new Movie("电影2","李四"));
        movies.add(new Movie("电影3","张三"));
        String star = "张三";

        //Stream写法:筛选演员包含star的电影,收集
        List<Movie> result = movies.stream()
                .filter(movie -> movie.getActor().contains(star))
                .collect(Collectors.toList());

        // 删除符合条件的电影(就是你之前removeIf底层就是Predicate流思想)
        movies.removeIf(movie -> movie.getActor().contains(star));
    }
}

八、Stream 常见坑点

  1. 流只能使用一次! 终端调用之后流失效,再次使用抛异常
java 复制代码
Stream<String> stream = Stream.of("a","b");
stream.forEach(System.out::println);
stream.count(); //报错 IllegalStateException
  1. 惰性求值陷阱:如果只有中间操作,没有终端操作,代码完全不执行!
java 复制代码
Stream.of(1,2,3).filter(n->{
    System.out.println(n);
    return n>1;
});
// 控制台无任何输出!没有终端操作,中间代码不运行
  1. distinct 自定义对象必须重写 equals 和 hashCode
  2. 并行流parallelStream()不要随便用,有线程安全问题,适合纯无状态数据。
  3. map 和 flatMap 容易混淆:map 一对一转换;flatMap 一对多,扁平化拆分流。

九、Stream 对比传统 for 循环

传统 for:怎么做(How),手动控制循环、索引、判断。

Stream:做什么(What),声明式编程,只写业务条件,不用关心循环细节,代码简洁。

复制代码
Stream流
├─定义:Java8,流水线,不存数据,惰性求值
├─获取流:Collection.stream()|Map转单列|Arrays.stream|Stream.of
├─中间操作(返回Stream)
│  ├─filter过滤、map转换、flatMap扁平化
│  ├─sorted排序、distinct去重、limit截取、skip跳过
├─终端操作(关闭流)
│  ├─collect收集、forEach遍历、count计数
│  ├─max/min、reduce归约、allMatch/anyMatch匹配
└─特性:一次性消费、不修改源数据、串行并行
相关推荐
估值探索者3 小时前
【Python量化系统工程实战 #08】从脚本到生产:量化系统上线 checklist 的最小闭环
java·开发语言·jvm·python·数据挖掘·数据·股票数据api接口
Wang's Blog3 小时前
Java框架 SpringCloud 快速入门: Nacos 环境隔离
java·开发语言·spring cloud
万年咸鱼3 小时前
解决C语言的内存释放机制
java·c语言·python
破芽3 小时前
python知识点整理02
开发语言·python
一木 之林3 小时前
DeepSeek Agent 开发(三)
java·大数据·linux
denlaku4 小时前
JDK 10 新特性详解
java·开发语言
cfm_29144 小时前
Java常见加密算法全解
开发语言
程序员耄耋4 小时前
JDK21的下载与安装(2025.8.2)
java
墨染天姬4 小时前
【人工智能训练师】python语法二
开发语言·人工智能·python