当前位置: 首页 > news >正文

Java Stream流:从入门到精通

Stream流,这是一个非常重要的里程碑。它代表了从“命令式编程”向“声明式编程”风格的转变,刚开始不理解是非常正常的。

一、Stream是做什么的?—— “流水线”的比喻

你可以把Stream想象成一条工厂里的流水线

  • 数据源(集合、数组等):就像是待加工的原材料,被放在流水线的开头。
  • 零个或多个“中间操作” (Intermediate operations):就像是流水线上的一道道加工工序,比如筛选、转换、排序等。这些工序不会立刻执行,只是被定义出来。
  • 一个“终结操作” (Terminal operation):就像是流水线的最后一道工序,比如打包、装箱。只有到了这一步,整个流水线才会被启动,所有原材料才会依次经过各个加工工序,最终产生结果。

Stream的核心思想是:你只需告诉它“做什么”(What),而不是“怎么做”(How)。

  • 传统循环(命令式):你写一个for循环,自己处理迭代、定义临时变量、写if条件判断。你是在指导计算机每一步该怎么执行。
  • Stream流(声明式):你直接说“帮我过滤出大于5的数,然后转换成字符串,最后排序并收集到一个列表里”。你只关心结果,而不关心内部是如何遍历和处理的。

举个例子:有一个数字列表,找出所有偶数,然后求它们的平方,最后收集到一个新列表里。

  • 传统方式(命令式):
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
    List<Integer> evenSquares = new ArrayList<>();
    for (Integer number : numbers) {if (number % 2 == 0) {         // 自己写if判断int square = number * number; // 自己计算平方evenSquares.add(square);    // 自己添加到新集合}
    }
    
  • Stream方式(声明式):
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
    List<Integer> evenSquares = numbers.stream() // 1. 获取流.filter(n -> n % 2 == 0)           // 2. “工序”一:过滤偶数.map(n -> n * n)                   // 3. “工序”二:映射为平方.collect(Collectors.toList());     // 4. “启动”流水线:收集结果
    

你看,Stream的代码更简洁、更易读,它的每一步操作都像在描述业务逻辑本身。


二、是任何类型都可以使用Stream吗?

不是任何类型本身都能用,但任何类型的【集合】或【数组】几乎都可以转换成Stream来处理。

Stream流本身是一个通用工具,但它不能直接作用于单个对象(比如你有一个String name = "Alice",你不能name.stream())。它的操作对象是数据序列

数据源主要来自以下几个方面:

  1. 从集合(Collection)来(最常见)

    • 所有Collection的实现类(List, Set, Queue等)都有.stream()方法。
    List<String> list = Arrays.asList("a", "b", "c");
    Stream<String> streamFromList = list.stream();Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
    Stream<Integer> streamFromSet = set.stream();
    
  2. 从数组来

    • 使用Arrays.stream(array)静态方法。
    int[] numbers = {1, 2, 3, 4, 5};
    IntStream streamFromArray = Arrays.stream(numbers); // 注意是IntStream
    
  3. 使用Stream类的静态方法

    • Stream.of(T... values): 直接用值创建流。
    • Stream.iterate()Stream.generate(): 创建无限流。
    Stream<String> streamOfValues = Stream.of("Java", "Python", "C++");// 创建一个无限的奇数流:1, 3, 5, 7...
    Stream<Integer> infiniteStream = Stream.iterate(1, n -> n + 2);
    
  4. 从文件等I/O资源来(比如Files.lines()):

    try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {lines.forEach(System.out::println);
    }
    
  5. 特别注意:基本类型

    • 直接操作基本类型(int, long, double)为了避免装箱拆箱的性能损耗,Java提供了特化的流:IntStream, LongStream, DoubleStream
    • 它们有额外的方法,如sum(), average(), range()等。

所以,结论是:你需要有一个数据序列(通常是集合或数组),然后就可以把它变成Stream来使用。


三、Stream的操作分类:中间操作 vs. 终结操作

这是理解Stream执行机制的关键。Stream的操作分为两大类:

类型特点常见方法返回值
中间操作懒惰的 (Lazy)filter(), map(), sorted(), distinct(), limit()返回一个新的Stream
不会立即执行,只是被记录下來flatMap(), peek()
终结操作积极的 (Eager)collect(), forEach(), count()返回一个非Stream的结果
会触发整个流水线的实际执行findFirst(), anyMatch(), reduce(), min()/max()(如void, List, Optional, int等)

执行原理:只有在调用终结操作时,所有中间操作才会组合成一个“流水线方案”,然后数据源中的元素会逐个地依次通过整个流水线。这种处理方式称为“循环融合”,效率很高,因为它只需要遍历一次集合。


四、常用的Stream操作(“加工工序”)

  1. filter(Predicate<? super T> predicate) - 过滤

    • 保留满足条件的元素。Predicate是一个返回boolean的函数。
    • .filter(s -> s.length() > 3) // 保留长度大于3的字符串
  2. map(Function<? super T, ? extends R> mapper) - 映射/转换

    • 将元素转换成另一种形式。Function是一个转换函数。
    • .map(String::toUpperCase) // 将每个字符串转为大写
    • .map(n -> n * 2) // 将每个数字乘以2
  3. sorted() / sorted(Comparator<? super T> comparator) - 排序

  4. distinct() - 去重

  5. limit(long maxSize) - 限制数量

  6. collect(Collector<? super T, A, R> collector) - 收集(最常用的终结操作)

    • 将流中的元素收集到各种不同的容器中(如List, Set, Map)。
    • .collect(Collectors.toList())
    • .collect(Collectors.toSet())
    • .collect(Collectors.joining(", ")) // 连接成字符串
    • .collect(Collectors.groupingBy(User::getDepartment)) // 按部门分组

总结与建议

  • Stream是什么:一个用于高效处理数据序列(特别是集合)的声明式API,遵循“流水线”模式。
  • 核心优势:代码简洁、可读性强、易于并行化(只需将.stream()换成.parallelStream())。
  • 使用条件:数据源需要是集合、数组等可以生成序列的类型。
  • 关键机制:操作分为中间操作(懒惰,定义工序)和终结操作(积极,启动执行)。

给你的学习建议

  1. 多练习从ListArray创建流。
  2. 重点掌握filtermapcollect这三个最常用的方法。
  3. 理解每个中间操作都会返回一个新流,但不会触发计算。
  4. 记住,没有终结操作,整个Stream流水线就什么都不会做

文章转载自:

http://FwGJXCKy.bzLfw.cn
http://N4XzemnG.bzLfw.cn
http://ZwHqsb9l.bzLfw.cn
http://tIRUOhdc.bzLfw.cn
http://C5mo5bUv.bzLfw.cn
http://U81jnko2.bzLfw.cn
http://IUrsiVTR.bzLfw.cn
http://iXszrfIb.bzLfw.cn
http://8sINMtu8.bzLfw.cn
http://tSJXO7Em.bzLfw.cn
http://fbdVEsDy.bzLfw.cn
http://5OglcmtB.bzLfw.cn
http://MjltsdBB.bzLfw.cn
http://9V2vgvt2.bzLfw.cn
http://vh5KYgXc.bzLfw.cn
http://nz77CLHa.bzLfw.cn
http://zCsPBrwc.bzLfw.cn
http://M951Jipj.bzLfw.cn
http://KNS0g9q5.bzLfw.cn
http://sjEvhc9R.bzLfw.cn
http://uy1MUJ5N.bzLfw.cn
http://dG64C4Nr.bzLfw.cn
http://gJ1xOy3j.bzLfw.cn
http://LeCRKDy0.bzLfw.cn
http://b3IYLJGp.bzLfw.cn
http://RJMpGRuc.bzLfw.cn
http://ySFtNUIC.bzLfw.cn
http://GHBfVXDK.bzLfw.cn
http://dKXJjTo7.bzLfw.cn
http://lQ7qjXua.bzLfw.cn
http://www.dtcms.com/a/371470.html

相关文章:

  • java常见面试题杂记
  • SAP匈牙利新闻
  • Java全栈工程师的面试实战:从基础到高阶技术解析
  • 计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】
  • 【嵌入式】【树莓派】【大疆PSDK】用树莓派4B开发大疆行业无人机应用系统小结-【硬件篇】
  • 深度学习——自然语言处理NLP
  • 灾难性遗忘:神经网络持续学习的核心挑战与解决方案
  • bug | 事务粒度不能太大,含demo
  • 如何建立针对 .NET Core web 程序的线程池的长期监控
  • 41个开源大语言模型基准测试报告
  • unsloth 笔记:从最近的检查点继续微调
  • 区域导航系统 | 印度区域卫星导航系统(IRNSS/NavIC)深度解析
  • Linux服务器资源自动监控与报警脚本详解
  • 社交新零售时代本地化微商的发展路径研究——基于开源AI智能名片链动2+1模式S2B2C商城小程序源的创新实践
  • Tailwind CSS v4 终极指南:体验 Rust 驱动的闪电般性能与现代化 CSS 工作流
  • 模块--红外跟随避障模块
  • 使用MQTT.fx和ESP32连接Onenet平台
  • 功率器件固晶挑战:抗高温翘曲治具提升IGBT焊接强度30%
  • Text2Sql.Net架构深度解析:从自然语言到SQL的智能转换之道
  • UE5 基础应用 —— 10 - 控制台命令
  • Linux内核Syncookies机制:抵御SYN Flood攻击的坚实防线
  • Axum 最佳实践:如何构建优雅的 Rust 错误处理系统?(三)
  • 使用 nginx-module-vts 进行 Nginx 流量监控
  • 心路历程-Linux如何赋予权限?
  • 实验室服务器配置|通过Docker实现Linux系统多用户隔离与安全防控
  • QProxyStyle类中drawControl和drawComplexControl函数的区别是什么
  • 【Linux手册】管道通信:从内核底层原理到使用方法
  • LeetCode 几道 Promises 和 Time 的题目
  • 狂想-机器人触感阵列理论验证方案
  • Unity 塔防自用可视化路点寻路编辑器