Java8新特性——Stream API常见操作

目录

一、什么是Stream?

1、Stream流的简介

[2、Stream 流的核心特点](#2、Stream 流的核心特点)

二、Stream流的创建

三、Stream常见操作

中间操作

终端操作


一、什么是Stream?

1、Stream流的简介

Stream 流是 Java 8 引入的一套数据处理工具,用于对集合、数组等数据源中的元素进行高效、简洁的处理。它并非数据结构(不存储数据),而是通过一系列链式操作(中间操作 + 终端操作),实现对数据的过滤、转换、聚合等处理,最终得到想要的结果。

简单来说,Stream 流就像一条 "流水线":数据源是 "原材料",中间操作是 "加工步骤",终端操作是 "最终产出"。它支持声明式编程风格(关注 "做什么" 而非 "怎么做"),大幅简化了集合数据的处理代码。

2、Stream 流的核心特点

  1. **不存储数据:**Stream 流本身不存储元素,元素来源于数据源(如集合、数组),它仅记录对数据的操作逻辑。

  2. **不修改源数据:**所有 Stream 操作不会改变原始数据源(如过滤集合不会删除原集合元素,映射操作不会修改原对象),而是生成新的结果。

  3. 延迟执行(Lazy Evaluation): 中间操作(如 filtermap)不会立即执行,只有当终端操作(如 collectforEach)被调用时,所有中间操作才会一次性执行("惰性求值")。这一特性可优化性能,避免无效计算。

  4. 内部迭代: 传统集合遍历依赖外部迭代(如 for 循环、forEach 循环,需手动控制遍历过程);而 Stream 采用内部迭代,遍历过程由 JVM 自动控制,开发者只需关注 "处理逻辑",无需关心 "如何遍历"。

  5. **一次性使用:**一个 Stream 流只能被遍历(执行终端操作)一次,遍历结束后即 "关闭",再次使用会抛出异常。若需重复处理,需重新创建 Stream。

  6. 可组合性: 中间操作返回的仍是 Stream 流,因此可通过链式调用组合多个操作(如 filter().map().sorted()),形成清晰的处理 pipeline,代码可读性更高。

  7. 支持并行处理: 可通过 parallelStream()stream.parallel() 轻松切换为并行流,利用多核 CPU 自动拆分任务并并行处理,无需手动编写多线程代码(底层基于 Fork/Join 框架)。

这些特点使得 Stream 流在处理集合数据时,比传统的循环 + 判断方式更简洁、高效,尤其适合复杂的数据处理场景(如统计、过滤、转换等)。

二、Stream流的创建

Stream 流的创建方式可简单总结为以下 6 类,核心围绕不同数据源展开:

  1. 从集合创建 :通过 Collection.stream()(串行流)或 parallelStream()(并行流),最常用,适用于内存集合数据。
  2. 从数组创建 :借助 Arrays.stream(),支持整个数组或指定子范围([start, end)),基本类型数组自动转为数值流。
  3. 从单个 / 多个值创建 :用 Stream.of() 处理零散值,支持可变参数。
  4. 空流创建Stream.empty() 用于无数据场景,替代 null 避免空指针。
  5. 无限流创建Stream.generate()(无规律)或 iterate()(有规律)生成无限元素,需配合 limit() 限制长度。
  6. 从文件创建Files.lines() 按行读取文本文件,返回每行内容的流,需注意资源关闭

三、Stream常见操作

Stream 操作分为中间操作 (返回 Stream,可链式调用)和终端操作(返回非 Stream,触发执行),是 Stream 流的核心能力。

中间操作

假设数据源:
List<Employee> employees = Arrays.asList(
new Employee("张三", 25, "研发部", 8000),
new Employee("李四", 30, "财务部", 12000),
new Employee("王五", 28, "研发部", 9500),
new Employee("赵六", 35, "研发部", 15000) );

1.过滤(filter)

  • 功能:保留满足 Predicate<T> 条件的元素
  • 示例:筛选 "研发部" 的员工
java 复制代码
Stream<Employee> devStream = employees.stream()
    .filter(emp -> "研发部".equals(emp.getDept()));

1.映射(map /flatMap)

  • map(Function<T, R>):将元素从 T 类型转换为 R 类型(一对一映射)
    • 示例:提取研发部员工的姓名(Employee → String)

      java 复制代码
      Stream<String> devNames = employees.stream()
          .filter(emp -> "研发部".equals(emp.getDept()))
          .map(Employee::getName); // 方法引用简化
    • flatMap(Function<T, Stream<R>>):将元素转换为 Stream,再合并为一个 Stream(一对多映射,解决 "嵌套集合" 问题)

      • 示例:处理 "员工列表→每个员工的技能列表→所有技能去重"

        java 复制代码
        // 假设 Employee 有 getSkills() 方法,返回 List<String>
        Stream<String> allSkills = employees.stream()
            .flatMap(emp -> emp.getSkills().stream()) // 拆分为单个技能的 Stream
            .distinct(); // 去重
  1. 排序(sorted)

    • 自然排序:sorted()(需元素实现 Comparable 接口)
    • 自定义排序:sorted(Comparator<T>)
      • 示例:按员工工资 "升序" 排序(工资相同按年龄 "降序")

        java 复制代码
        Stream<Employee> sortedStream = employees.stream()
            .sorted(Comparator.comparingInt(Employee::getSalary) // 工资升序
                .thenComparing(Comparator.comparingInt(Employee::getAge).reversed())); // 年龄降序
  2. 去重(distinct)

    • 功能:基于 equals() 方法去重(若为自定义对象,需重写 equals()hashCode()

    • 示例:筛选不重复的部门名称

      java 复制代码
      Stream<String> depts = employees.stream()
          .map(Employee::getDept)
          .distinct();
  3. 限制(limit)与跳过(skip)

    • limit(long n):保留前 n 个元素(n 为 0 时返回空 Stream)

    • skip(long n):跳过前 n 个元素(n 超过元素总数时返回空 Stream)

    • 示例:获取 "研发部" 工资前 2 名的员工(先筛选→排序→取前 2)

      复制代码
      Stream<Employee> top2Dev = employees.stream()
          .filter(emp -> "研发部".equals(emp.getDept()))
          .sorted(Comparator.comparingInt(Employee::getSalary).reversed())
          .limit(2);

终端操作

  1. 遍历(forEach /forEachOrdered)

    • forEach(Consumer<T>):消费元素(并行流中顺序不确定)

    • forEachOrdered(Consumer<T>):并行流中保证元素顺序(性能略低)

    • 示例:打印研发部员工信息

      复制代码
      employees.stream()
          .filter(emp -> "研发部".equals(emp.getDept()))
          .forEach(emp -> System.out.println(emp.getName() + ":" + emp.getSalary()));
  2. 收集(collect)------ 最常用终端操作

    • 功能:将 Stream 元素转换为集合、Map、String 等(依赖 Collectors 工具类)

    • 常用 Collectors 方法:

      方法 功能 示例
      toList() 转换为 List(默认 ArrayList) List<String> names = stream.collect(Collectors.toList());
      toSet() 转换为 Set(默认 HashSet) Set<String> depts = stream.collect(Collectors.toSet());
      toMap(keyMapper, valueMapper) 转换为 Map(需避免 key 重复) Map<String, Integer> nameSalary = stream.collect(Collectors.toMap(Employee::getName, Employee::getSalary));
      joining(separator) 拼接字符串(适用于 String 流) String allNames = stream.collect(Collectors.joining(","));
      groupingBy(classifier) 按分类器分组(返回 Map <分类值,List>) Map<String, List<Employee>> deptEmp = stream.collect(Collectors.groupingBy(Employee::getDept));
      partitioningBy(predicate) 按布尔条件分区(返回 Map<Boolean, List>) Map<Boolean, List<Employee>> highSalary = stream.collect(Collectors.partitioningBy(emp -> emp.getSalary() > 10000));
  3. 查找(findFirst /findAny)

    • findFirst():返回 Stream 中第一个元素(有序流常用,返回 Optional)

    • findAny():返回 Stream 中任意一个元素(并行流效率高,返回 Optional)

    • 示例:查找任意一个研发部员工

      复制代码
      Optional<Employee> devEmp = employees.stream()
          .filter(emp -> "研发部".equals(emp.getDept()))
          .findAny();
      // 安全获取值(避免 NPE)
      devEmp.ifPresent(emp -> System.out.println(emp.getName()));
  4. 匹配(allMatch /anyMatch/noneMatch)

    • 功能:判断元素是否满足条件(返回 boolean)

    • allMatch(Predicate<T>):所有元素满足条件 → true

    • anyMatch(Predicate<T>):至少一个元素满足条件 → true

    • noneMatch(Predicate<T>):所有元素不满足条件 → true

    • 示例:判断 "财务部是否有员工工资超过 15000"

      复制代码
      boolean hasHighSalary = employees.stream()
          .filter(emp -> "财务部".equals(emp.getDept()))
          .anyMatch(emp -> emp.getSalary() > 15000);
  5. 统计(count /max/min / 数值统计)

    • count():返回元素总数(long 类型)
    • max(Comparator<T>) / min(Comparator<T>):返回最大 / 最小元素(Optional)
    • 数值统计:mapToInt()/mapToLong()/mapToDouble() 转换为数值流,再用 summaryStatistics() 获取完整统计
      • 示例:统计研发部员工的工资信息(平均、最高、总和)

        复制代码
        IntSummaryStatistics salaryStats = employees.stream()
            .filter(emp -> "研发部".equals(emp.getDept()))
            .mapToInt(Employee::getSalary) // 转换为 IntStream
            .summaryStatistics();
        System.out.println("平均工资:" + salaryStats.getAverage());
        System.out.println("最高工资:" + salaryStats.getMax());
        System.out.println("工资总和:" + salaryStats.getSum());
  6. 归约(reduce)

    • 功能:将 Stream 元素 "合并" 为单个值(如求和、求乘积)

    • 重载方法:

      • reduce(T identity, BinaryOperator<T> accumulator):有初始值(返回 T,无 null 风险)
      • reduce(BinaryOperator<T> accumulator):无初始值(返回 Optional,避免无元素时返回 null)
    • 示例:计算研发部员工工资总和

      复制代码
      // 有初始值(0),返回 int
      int totalSalary = employees.stream()
          .filter(emp -> "研发部".equals(emp.getDept()))
          .mapToInt(Employee::getSalary)
          .reduce(0, Integer::sum); // 0 是初始值,sum 是累加器
相关推荐
Java水解2 小时前
100道互联网大厂面试题+答案
java·后端·面试
nlog3n2 小时前
分布式计数器系统完整解决方案
java·分布式
ytadpole2 小时前
Java并发编程:从源码分析ThreadPoolExecutor 的三大核心机制
java·面试
Aevget2 小时前
「Java EE开发指南」用MyEclipse开发的EJB开发工具(一)
java·ide·java-ee·myeclipse
TanYYF2 小时前
Spring Boot 异步处理框架核心源码解析及实现原理
java·spring boot·spring
百锦再3 小时前
从 .NET 到 Java 的转型指南:详细学习路线与实践建议
android·java·前端·数据库·学习·.net·数据库架构
z晨晨3 小时前
互联网大厂Java求职面试场景
java·redis·spring·面试·多线程·互联网大厂
方圆想当图灵3 小时前
深入浅出 gRPC
java·后端·github
卷Java3 小时前
uni-app 模板语法修复说明
java·数据库·spring boot·uni-app·mybatis