【Java基础|Stream流:从基础入门到实战进阶,告别繁琐循环!】
前言
在Java 8之前,我们对集合、数组进行过滤、遍历、排序、分组、求和等操作时,往往需要编写大量冗余的for循环、迭代器代码,不仅代码繁琐、可读性差,还极易出错,并行处理也需要手动编写多线程代码,开发效率极低。
Java 8 推出的Stream流,彻底改变了传统集合操作的弊端,基于函数式编程思想,提供了一套声明式、链式、简洁高效的数据处理方案,让我们只需关注业务逻辑,不用关心底层遍历细节,轻松实现复杂的数据处理,同时原生支持并行流,大幅提升数据处理性能。
本篇文章带你从零开始全面掌握Java Stream流,涵盖核心概念、流创建、中间操作、终端操作、实战案例、常见坑点、性能优化,学完就能在项目中落地使用,彻底告别臃肿的循环代码!
一、Stream流核心概念
1.1 什么是Stream流
Stream是Java 8引入的函数式编程核心特性,官方定义:支持串行和并行聚合操作的元素序列。
简单理解:Stream是数据处理流水线,基于数据源(集合、数组、文件等),通过一系列链式操作,完成数据的筛选、转换、统计、收集等操作,最终得到我们想要的结果。
关键注意事项:
1. Stream不是数据结构,不存储数据,只负责数据处理
2. 不修改原数据源,所有操作都是生成新的数据,保证数据源安全
3. 惰性执行(延迟加载):中间操作不会执行,只有触发终端操作才会执行
4. 一次性使用:一个Stream只能被操作一次,用完即销毁
5. 内部迭代:无需手动编写循环,底层自动完成迭代
1.2 Stream流 VS 传统循环
- 传统循环:代码冗余、可读性差、维护困难、手动迭代、并行实现复杂
- Stream流:代码简洁、声明式编程、可读性极高、内置优化、并行流开箱即用
二、Stream流完整执行流程
Stream流操作分为两大步骤,缺一不可:
1. 创建Stream:通过数据源获取流对象
2. 中间操作:对数据进行过滤、映射、排序等链式处理,可叠加多个操作
3. 终端操作:触发流执行,最终输出结果、收集数据,执行后流关闭
只有中间操作不会执行任何逻辑,必须添加终端操作才会真正处理数据!
三、Stream流的4种创建方式
日常开发中,这几种创建方式完全够用,直接上代码示例:
1. 集合创建(最常用)
// List集合转流<String> list =<>(); Stream<String> stream = list.stream(); // 串行流 <String> parallelStream = list.parallelStream(); // 并行流 // Set<String<><String> setStream = set.stream();
2. 数组创建
String[] arr = {"Java", "Stream", "Spring", "MyBatis<String> arrStream = Arrays.stream(arr);
3. Stream静态方法of
ofStream = Stream.of("Java", "Stream", "Spring");四、Stream常用中间操作
中间操作返回新的Stream,可链式拼接,惰性执行,分为筛选、切片、映射、排序、去重几大类。
4.1 筛选与切片
方法 作用
filter(Predicate) 按条件过滤数据,保留符合条件的元素
limit(long) 截取前n个元素
skip(long) 跳过前n个元素
distinct() 去除重复元素(基于equals和hashCode)
代码示例:
numList.stream() .filter(num -> num > 3) // 过滤大于3的数字 .distinct() // 去重 .skip(1) // 跳过1个 .limit(3) // 截取3个 .forEach(System.out::println);4.2 映射转换
方法 作用
map(Function) 将元素转换为其他类型/提取属性
flatMap(Function) 扁平化流,拆解嵌套集合
代码示例:
// 提取对象属性<User> userList = Arrays.asList( new User("张三", 22), new User("李四", 25), new User("王五", 20) ); // 提取所有用户姓名 List<String> nameList = userList.stream() .map(User::getName) .collect(Collectors.toList()); // 扁平化流:拆解多个集合 <String>> listList = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "<String> flatList = listList.stream() .flatMap(Collection::stream) .collect(Collectors.toList());4.3 排序
方法 作用
sorted() 自然排序(实现Comparable接口)
sorted(Comparator) 自定义条件排序
代码示例:
// 年龄降序排序 List<User> sortedList = userList.stream() .sorted(Comparator.comparing(User::getAge).reversed()) .collect(Collectors.toList());五、Stream常用终端操作
终端操作会触发整个流执行,执行完毕后流关闭,分为遍历、统计、查找、匹配、收集五大类。
5.1 遍历消费
// 遍历输出 stream.forEach(System.out::println);5.2 查找与匹配
// 判断是否有任意元素符合条件 boolean anyMatch = userList.stream().anyMatch(user -> user.getAge() > 20); // 判断所有元素都符合条件 boolean allMatch = userList.stream().allMatch(user -> user.getAge() > 18); // 判断都不符合条件 boolean noneMatch = userList.stream().noneMatch(user -> user.getAge() > 30); // 获取第一个元素<User> firstUser = userList.stream().findFirst(); // 获取任意<User> anyUser = userList.stream().findAny();5.3 聚合统计
// 最大值、最小值、求和、计数、平均值 int maxAge = userList.stream().mapToInt(User::getAge).max().getAsInt(); int minAge = userList.stream().mapToInt(User::getAge).min().getAsInt(); int sumAge = userList.stream().mapToInt(User::getAge).sum(); long count = userList.stream().count(); double avgAge = userList.stream().mapToInt(User::getAge).average().getAsDouble();5.4 归约reduce
聚合计算,多用于求和、求最值、拼接字符串:
// 数字求和 int total = Stream.of(1,2,3,4,5).reduce(0, Integer::sum);5.5 收集collect(最核心)
将流转换为集合、字符串、分组、分区,开发中使用频率最高!
// 转<User> users = stream.collect(Collectors.toList()); // 转<String> names = stream.map(User::getName).collect(Collectors.toSet()); // 转Map<String, Integer> userMap = userList.stream().collect(Collectors.toMap(User::getName, User::getAge)); // 字符串拼接 String nameStr = userList.stream().map(User::getName).collect(Collectors.joining(",")); // 分组(按年龄分组) Map<User>> groupByAge = userList.stream().collect(Collectors.groupingBy(User::getAge)); // 分区(满足条件一个集合,不满足一个集合) Map<User>> partition = userList.stream().collect(Collectors.partitioningBy(user -> user.getAge() > 22));六、企业级实战案例
直接贴合开发场景,拿来就能用!
案例1:筛选符合条件的用户数据
// 筛选年龄20-30岁,姓名不为空,按年龄升序,提取姓名集合 <String> resultList = userList.stream() .filter(user -> user.getAge() >= 20<= 30) .filter(user -> StringUtils.isNotBlank(user.getName())) .sorted(Comparator.comparing(User::getAge)) .map(User::getName) .collect(Collectors.toList());
案例2:数据分组统计
// 按年龄分组,统计每组人数、平均年龄 Map<Integer, Long> ageCountMap = userList.stream() .collect(Collectors.groupingBy(User::getAge, Collectors.counting()));
案例3:数组/集合去重+排序
result = Arrays.asList(3,1,2,2,5,4,3) .stream() .distinct() .sorted() .collect(Collectors.toList());七、Stream流并行流使用
7.1 什么是并行流
底层自动使用**ForkJoin线程池**,多线程并行处理数据,**无需手动写多线程**,大幅提升大数据量处理效率。
7.2 使用方式
// 直接获取并行流 list.parallelStream() // 串行流转并行流 list.stream().parallel()7.3使用场景
- 数据量较大的集合操作
- 无状态、无顺序依赖的数据处理
不适用场景:数据量小、有顺序要求、线程不安全的操作
八、Stream流常见坑点与避坑指南
1. Stream只能使用一次,重复操作会报 IllegalStateException
2. 中间操作惰性执行,必须加终端操作,否则代码不执行
3. 不修改原数据,但如果流中是对象,可修改对象属性,注意对象引用问题
4. 并行流慎用,线程不安全场景会导致数据错乱
5. flatMap避免嵌套过深,降低代码可读性
6. Optional接收结果,避免空指针异常,不要直接get()
九、总结
Stream流核心优势
1. 代码极简,可读性拉满,告别冗余for循环
2. 函数式编程,逻辑清晰,专注业务而非遍历
3. 内置优化,性能优于手动循环
4. 并行流开箱即用,轻松实现高性能数据处理
5. 支持链式编程,代码优雅易维护
学习路线
创建流 → 中间操作(筛选/映射/排序) → 终端操作(收集/统计/遍历) → 实战进阶 → 并行流优化
Java Stream流是后端开发必备技能,熟练使用后,能大幅提升代码质量和开发效率,日常业务中的集合数据处理,优先使用Stream实现,赶紧上手练习吧!
PS:建议结合Lambda表达式、Optional、方法引用一起学习,函数式编程效果更佳,后续会更新Stream高级用法与源码原理!
