目录
[2、Stream 流的核心特点](#2、Stream 流的核心特点)
一、什么是Stream?
1、Stream流的简介
Stream 流是 Java 8 引入的一套数据处理工具,用于对集合、数组等数据源中的元素进行高效、简洁的处理。它并非数据结构(不存储数据),而是通过一系列链式操作(中间操作 + 终端操作),实现对数据的过滤、转换、聚合等处理,最终得到想要的结果。
简单来说,Stream 流就像一条 "流水线":数据源是 "原材料",中间操作是 "加工步骤",终端操作是 "最终产出"。它支持声明式编程风格(关注 "做什么" 而非 "怎么做"),大幅简化了集合数据的处理代码。
2、Stream 流的核心特点
-
**不存储数据:**Stream 流本身不存储元素,元素来源于数据源(如集合、数组),它仅记录对数据的操作逻辑。
-
**不修改源数据:**所有 Stream 操作不会改变原始数据源(如过滤集合不会删除原集合元素,映射操作不会修改原对象),而是生成新的结果。
-
延迟执行(Lazy Evaluation): 中间操作(如
filter
、map
)不会立即执行,只有当终端操作(如collect
、forEach
)被调用时,所有中间操作才会一次性执行("惰性求值")。这一特性可优化性能,避免无效计算。 -
内部迭代: 传统集合遍历依赖外部迭代(如
for
循环、forEach
循环,需手动控制遍历过程);而 Stream 采用内部迭代,遍历过程由 JVM 自动控制,开发者只需关注 "处理逻辑",无需关心 "如何遍历"。 -
**一次性使用:**一个 Stream 流只能被遍历(执行终端操作)一次,遍历结束后即 "关闭",再次使用会抛出异常。若需重复处理,需重新创建 Stream。
-
可组合性: 中间操作返回的仍是 Stream 流,因此可通过链式调用组合多个操作(如
filter().map().sorted()
),形成清晰的处理 pipeline,代码可读性更高。 -
支持并行处理: 可通过
parallelStream()
或stream.parallel()
轻松切换为并行流,利用多核 CPU 自动拆分任务并并行处理,无需手动编写多线程代码(底层基于 Fork/Join 框架)。
这些特点使得 Stream 流在处理集合数据时,比传统的循环 + 判断方式更简洁、高效,尤其适合复杂的数据处理场景(如统计、过滤、转换等)。
二、Stream流的创建
Stream 流的创建方式可简单总结为以下 6 类,核心围绕不同数据源展开:
- 从集合创建 :通过
Collection.stream()
(串行流)或parallelStream()
(并行流),最常用,适用于内存集合数据。 - 从数组创建 :借助
Arrays.stream()
,支持整个数组或指定子范围([start, end)
),基本类型数组自动转为数值流。 - 从单个 / 多个值创建 :用
Stream.of()
处理零散值,支持可变参数。 - 空流创建 :
Stream.empty()
用于无数据场景,替代null
避免空指针。 - 无限流创建 :
Stream.generate()
(无规律)或iterate()
(有规律)生成无限元素,需配合limit()
限制长度。 - 从文件创建 :
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)
javaStream<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(); // 去重
-
-
-
排序(sorted)
- 自然排序:
sorted()
(需元素实现Comparable
接口) - 自定义排序:
sorted(Comparator<T>)
-
示例:按员工工资 "升序" 排序(工资相同按年龄 "降序")
javaStream<Employee> sortedStream = employees.stream() .sorted(Comparator.comparingInt(Employee::getSalary) // 工资升序 .thenComparing(Comparator.comparingInt(Employee::getAge).reversed())); // 年龄降序
-
- 自然排序:
-
去重(distinct)
-
功能:基于
equals()
方法去重(若为自定义对象,需重写equals()
和hashCode()
) -
示例:筛选不重复的部门名称
javaStream<String> depts = employees.stream() .map(Employee::getDept) .distinct();
-
-
限制(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);
-
终端操作
-
遍历(forEach /forEachOrdered)
-
forEach(Consumer<T>)
:消费元素(并行流中顺序不确定) -
forEachOrdered(Consumer<T>)
:并行流中保证元素顺序(性能略低) -
示例:打印研发部员工信息
employees.stream() .filter(emp -> "研发部".equals(emp.getDept())) .forEach(emp -> System.out.println(emp.getName() + ":" + emp.getSalary()));
-
-
收集(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));
-
-
查找(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()));
-
-
匹配(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);
-
-
统计(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());
-
-
归约(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 是累加器
-