欢迎光临
我们一直在努力

JavaSE加强:Stream流

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

包:java.util.stream.*

一、Stream 流定义与核心概念

1. 什么是 Stream 流

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

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

2. Stream 五大核心特性

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

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

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

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

    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 的元素

    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 接口)

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

    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 扁平化映射

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

    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):自定义比较器排序(推荐)

    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()方法

    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:直接比较存储地址变为先比较地址,然后保证参数对象不为空且属于实体类,最后返回属性比较的布尔值

    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 个元素

    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 个元素

    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 调试用,遍历元素,不修改流

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

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

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

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

    Stream.of("A","B","C").forEach(System.out::println);

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

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

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

    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():字符串拼接

    String str = Stream.of("张三","李四","王五").collect(Collectors.joining(","));
    System.out.println(str); //张三,李四,王五

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

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

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

    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 归约、聚合,把流元素反复合并成一个结果

    求和、求乘积

    // 参数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

    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 转为原始流

    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

    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 常见坑点

  • 流只能使用一次! 终端调用之后流失效,再次使用抛异常
  • Stream<String> stream = Stream.of("a","b");
    stream.forEach(System.out::println);
    stream.count(); //报错 IllegalStateException

  • 惰性求值陷阱:如果只有中间操作,没有终端操作,代码完全不执行!
  • Stream.of(1,2,3).filter(n->{
    System.out.println(n);
    return n>1;
    });
    // 控制台无任何输出!没有终端操作,中间代码不运行

  • distinct 自定义对象必须重写 equals 和 hashCode
  • 并行流parallelStream()不要随便用,有线程安全问题,适合纯无状态数据。
  • 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匹配
    └─特性:一次性消费、不修改源数据、串行并行

    赞(0)
    未经允许不得转载:171主机测评 » JavaSE加强:Stream流
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址