基础知识-java流steam

Java Stream 流详解

一、Stream 概述

创建
中间操作
终端操作
集合/数组
Stream
结果

Stream(流)是 Java 8 引入的一个新的抽象层,它允许以声明性方式处理数据集合。Stream API 旨在让编程更加关注数据处理的 “做什么” 而不是 “怎么做”

特点

  • 非存储: Stream 不是数据结构,不存储元素
  • 函数式: Stream 操作不会修改源数据
  • 惰性求值: 中间操作不会立即执行,而是等到终端操作时才执行
  • 可消费性: 一个 Stream 只能被消费(遍历)一次
  • 并行处理: 可以轻松地实现并行处理

与集合的区别

流Stream
集合Collection
处理元素
内部迭代
声明式处理
存储元素
外部迭代
命令式处理
特性集合 Collection流 Stream
数据存储存储元素不存储元素
迭代方式外部迭代(显式控制)内部迭代(隐式控制)
处理方式命令式(How to do)声明式(What to do)
使用次数可多次使用只能使用一次
处理时机立即处理延迟处理(惰性求值)

二、Stream 创建方式

1. 从集合创建

// 从List创建Stream
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();// 从Set创建Stream
Set<String> set = new HashSet<>(Arrays.asList("a", "b", "c"));
Stream<String> stream = set.stream();// 从Map创建Stream
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);// 获取键的Stream
Stream<String> keyStream = map.keySet().stream();
// 获取值的Stream
Stream<Integer> valueStream = map.values().stream();
// 获取键值对的Stream
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

2. 从数组创建

// 从数组创建Stream
String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);// 指定范围
Stream<String> stream = Arrays.stream(arr, 0, 2); // 只包含 a 和 b

3. 从值创建

Stream<String> stream = Stream.of("a", "b", "c");

4. 使用生成器

// 创建无限流,使用limit限制元素数量
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5); // 0, 2, 4, 6, 8
Stream<Double> stream = Stream.generate(Math::random).limit(5); // 5个随机数

5. 从文件创建

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {lines.forEach(System.out::println);
}

6. 创建空 Stream

Stream<String> emptyStream = Stream.empty();

三、Stream 操作类型

Stream 操作可分为两大类:中间操作(Intermediate Operations)和终端操作(Terminal Operations)。

1. 中间操作(返回 Stream)

中间操作用于对 Stream 进行变换和筛选,但不会执行计算,而是记住要进行的操作,返回一个新的 Stream。

filter()
map()
sorted()
Stream<T>
Stream<T>
Stream<R>
Stream<R>

2. 终端操作(返回非 Stream)

终端操作会从 Stream 中生成结果,执行终端操作后,Stream 被消费,不能再使用。

collect()
forEach()
reduce()
Stream<T>
Collection<T>
void
Optional<T>

四、常用 Stream 操作

1. 中间操作

过滤和筛选
方法描述示例
filter()过滤元素stream.filter(n -> n > 10)
distinct()去除重复元素stream.distinct()
limit()限制元素数量stream.limit(5)
skip()跳过前 n 个元素stream.skip(3)
映射和转换
方法描述示例
map()转换元素stream.map(String::toUpperCase)
flatMap()扁平化嵌套流stream.flatMap(List::stream)
mapToInt()转换为 IntStreamstream.mapToInt(String::length)
mapToLong()转换为 LongStreamstream.mapToLong(x -> (long)x)
mapToDouble()转换为 DoubleStreamstream.mapToDouble(x -> (double)x)
排序
方法描述示例
sorted()自然顺序排序stream.sorted()
sorted(Comparator)自定义排序stream.sorted(Comparator.reverseOrder())
查看元素
方法描述示例
peek()查看元素(调试用)stream.peek(System.out::println)

2. 终端操作

遍历
方法描述示例
forEach()遍历每个元素stream.forEach(System.out::println)
forEachOrdered()按顺序遍历每个元素stream.forEachOrdered(System.out::println)
聚合
方法描述示例
count()计算元素数量stream.count()
max()获取最大值stream.max(Comparator.naturalOrder())
min()获取最小值stream.min(Comparator.naturalOrder())
reduce()归约操作stream.reduce(0, Integer::sum)
收集
方法描述示例
collect()收集到集合中stream.collect(Collectors.toList())
toArray()转换为数组stream.toArray()
匹配
方法描述示例
anyMatch()任一元素匹配stream.anyMatch(n -> n > 10)
allMatch()所有元素匹配stream.allMatch(n -> n > 0)
noneMatch()没有元素匹配stream.noneMatch(n -> n < 0)
查找
方法描述示例
findFirst()获取第一个元素stream.findFirst()
findAny()获取任意元素stream.findAny()

五、Collectors 收集器

Collectors 类提供了许多静态工厂方法,用于创建收集器。常用的收集器如下:

1. 收集到集合

// 收集到List
List<String> list = stream.collect(Collectors.toList());// 收集到Set
Set<String> set = stream.collect(Collectors.toSet());// 收集到特定集合
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));

2. 转换为字符串

// 转换为逗号分隔的字符串
String joined = stream.collect(Collectors.joining(", "));

3. 统计

// 基本统计
IntSummaryStatistics stats = stream.collect(Collectors.summarizingInt(String::length));
double average = stats.getAverage();
int max = stats.getMax();

4. 分组和分区

// 按长度分组
Map<Integer, List<String>> groupedByLength =stream.collect(Collectors.groupingBy(String::length));// 分区(按条件分为两组)
Map<Boolean, List<String>> partitioned =stream.collect(Collectors.partitioningBy(s -> s.length() > 3));

5. 聚合

// 计算总和
int total = stream.collect(Collectors.summingInt(String::length));// 计算平均值
double avg = stream.collect(Collectors.averagingInt(String::length));// 找出最大值
Optional<String> longest = stream.collect(Collectors.maxBy(Comparator.comparing(String::length)));

六、Stream 与集合结合使用的案例

案例 1: 过滤和转换

List<Person> persons = Arrays.asList(new Person("Alice", 25),new Person("Bob", 30),new Person("Charlie", 35),new Person("David", 40)
);// 找出年龄大于30的人的名字,并转换为大写
List<String> names = persons.stream().filter(p -> p.getAge() > 30).map(Person::getName).map(String::toUpperCase).collect(Collectors.toList());// 结果: [CHARLIE, DAVID]

案例 2: 分组和统计

List<Product> products = Arrays.asList(new Product("Apple", "Fruit", 1.5),new Product("Banana", "Fruit", 0.8),new Product("Carrot", "Vegetable", 0.5),new Product("Potato", "Vegetable", 0.4)
);// 按类别分组并计算平均价格
Map<String, Double> avgPriceByCategory = products.stream().collect(Collectors.groupingBy(Product::getCategory,Collectors.averagingDouble(Product::getPrice)));// 结果: {Fruit=1.15, Vegetable=0.45}

案例 3: 扁平化处理嵌套集合

List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1, 2, 3),Arrays.asList(4, 5, 6),Arrays.asList(7, 8, 9)
);// 将嵌套列表扁平化处理并计算总和
int sum = nestedList.stream().flatMap(List::stream).mapToInt(Integer::intValue).sum();// 结果: 45

案例 4: 排序和限制

List<User> users = getUsers(); // 假设这个方法返回一个用户列表// 找出前三个年龄最大的用户
List<User> topThreeOldestUsers = users.stream().sorted(Comparator.comparing(User::getAge).reversed()).limit(3).collect(Collectors.toList());

案例 5: 使用 reduce 进行自定义聚合

List<Order> orders = getOrders(); // 假设这个方法返回一个订单列表// 计算所有订单的总金额
double totalAmount = orders.stream().map(Order::getAmount).reduce(0.0, Double::sum);

案例 6: 并行处理提高性能

// 串行处理
long count = numbers.stream().filter(n -> n > 1000).count();// 并行处理
long count = numbers.parallelStream().filter(n -> n > 1000).count();

七、primitive 流

Java 8 提供了专门用于处理基本数据类型的流:IntStreamLongStreamDoubleStream。这些流提供了针对基本类型的特殊操作,避免了装箱和拆箱的开销。

创建基本类型流

// 从数组创建
int[] numbers = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(numbers);// 使用范围
IntStream range = IntStream.range(1, 6);      // 1, 2, 3, 4, 5
IntStream rangeClosed = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5

基本类型流的特殊操作

// 数学操作
OptionalDouble avg = IntStream.rangeClosed(1, 10).average();
int sum = IntStream.rangeClosed(1, 10).sum();
OptionalInt max = IntStream.rangeClosed(1, 10).max();// 转换为对象流
Stream<Integer> boxed = IntStream.rangeClosed(1, 10).boxed();

八、并行流

并行流允许并行处理流中的元素,适用于大型数据集和计算密集型任务。

创建并行流

// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> parallelStream = list.parallelStream();// 从顺序流转换
Stream<String> parallel = list.stream().parallel();

并行流的注意事项

  1. 状态依赖操作: 避免使用依赖于元素顺序或之前元素处理结果的操作
  2. 副作用: 避免修改共享状态
  3. 合并成本: 某些情况下合并结果的成本可能高于并行处理带来的收益
  4. 数据量: 对于小数据集,并行处理可能比顺序处理更慢
  5. 线程安全: 确保在使用并行流时的操作是线程安全的
// 不安全的并行操作(有副作用)
List<Integer> numbers = new ArrayList<>();
IntStream.range(0, 1000).parallel().forEach(numbers::add); // 不要这样做!// 安全的并行操作
List<Integer> numbers = IntStream.range(0, 1000).parallel().boxed().collect(Collectors.toList());

九、Stream 性能考虑

1. 何时使用流

  • 处理集合元素时,尤其是进行过滤、转换、分组等操作
  • 需要链式处理数据时
  • 需要进行函数式和声明式编程时

2. 何时不使用流

  • 对于简单的循环操作,传统的 for 循环可能更高效
  • 需要直接修改元素时
  • 需要使用索引进行复杂操作时
  • 需要多次遍历同一集合时

3. 性能优化技巧

  1. 减少中间操作: 尽量减少中间操作的数量,尤其是会生成新集合的操作
  2. 适当使用并行流: 对于大型数据集,考虑使用并行流
  3. 短路操作: 使用 limit(), findFirst() 等短路操作可以在满足条件时立即结束处理
  4. 优化操作顺序: 将过滤操作放在前面可以减少后续操作的数据量
  5. 避免不必要的装箱/拆箱: 使用专门的基本类型流
// 优化前
stream.map(String::length).filter(l -> l > 5).limit(10);// 优化后 - 先过滤再映射,减少映射操作的数据量
stream.filter(s -> s.length() > 5).limit(10).map(String::length);

十、常见问题与最佳实践

1. Stream 只能使用一次

Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 错误: stream已被消费

2. 避免在流操作中修改源数据

// 错误做法
list.stream().forEach(list::remove); // 不要这样做!// 正确做法
List<String> filtered = list.stream().filter(predicate).collect(Collectors.toList());

3. 注意避免无限流导致的问题

// 无限流必须使用limit等操作限制元素数量
Stream.iterate(0, n -> n + 1).limit(10) // 没有这行会导致无限循环.forEach(System.out::println);

4. 使用合适的收集器

// 不要盲目收集到List,根据需要选择合适的集合类型
Set<String> uniqueNames = stream.collect(Collectors.toSet()); // 如果需要去重

5. 函数保持纯净

// 纯函数,无副作用
stream.map(s -> s.toUpperCase()).collect(Collectors.toList());// 有副作用的函数,应避免
List<String> result = new ArrayList<>();
stream.forEach(s -> result.add(s.toUpperCase())); // 不要这样做!

总结

Java Stream API 提供了一种简洁、声明式的方式来处理集合数据,使代码更加清晰、简洁。通过合理使用 Stream 的各种操作,可以大大提高编码效率和代码可读性。但也需注意 Stream 的使用场景和性能考虑,在合适的场景选择合适的工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78904.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.Android(通过Manifest配置文件传递数据(meta-data))

配置文件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"><applicationandroid:allowBackup"tr…

java 解析入参里的cron表达式,修改周时间

文章目录 前言一、java 解析入参里的cron表达式,修改周时间二、使用步骤1.示例 总结 前言 一、java 解析入参里的cron表达式,修改周时间 示例&#xff1a; 第一种: 0 0 0,16 ? * 0,1 第2种 0 0 0,16 ? * 1-7 第3种 0 0 0,16 ? * ? 第4种 0 0 0,16 ? * * 二、使用步骤 1…

DTO,VO,PO,Entity

1. DTO (Data Transfer Object) 定义 DTO 是数据传输对象&#xff0c;用于在不同系统或层之间传输数据。 目的 简化数据传输&#xff0c;降低耦合&#xff0c;通常只包含需要传输的字段&#xff0c;避免暴露内部实现细节。 使用场景 Controller 和 Service 或 远程调用 之…

从零搭建高可用分布式限流组件:设计模式与Redis令牌桶实践

一、需求背景与设计目标 在分布式系统中&#xff0c;面对突发流量时需要一种精准可控的流量控制手段。我们的组件需要具备&#xff1a; 多维度限流&#xff08;用户/IP/服务节点/自定义表达式&#xff09;分布式环境下精准控制开箱即用的Spring Boot Starter集成高扩展性的架…

Node.js 事件循环和线程池任务完整指南​

在 Node.js 的运行体系中&#xff0c;事件循环和线程池是保障其高效异步处理能力的核心组件。事件循环负责调度各类异步任务的执行顺序&#xff0c;而线程池则承担着处理 CPU 密集型及部分特定 I/O 任务的工作。接下来&#xff0c;我们将结合图示&#xff0c;详细剖析两者的工作…

echarts自定义图表--仪表盘

基于仪表盘类型的自定义表盘 上图为3层结构组成 正常一个仪表盘配置要在外圈和内圈之间制造一条缝隙间隔 再创建一个仪表盘配置 背景透明 进度条拉满 进度条颜色和数据的背景相同开始处的线 又一个仪表盘配置 数值固定一个比较小的值 <!DOCTYPE html> <html><h…

【数据结构】图论存储结构深度解析:邻接多重表如何实现无向图O(1)删边?邻接矩阵/链表/十字链对比

邻接多重表 导读一、有向图的存储结构二、邻接多重表三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、四种存储方式的总结5.1 空间复杂度5.2 找相邻边5.3 删除边或结点5.4 适用于5.5 表示方式 六、图的基本操作结语 导读 大家好&#xff0c;很高兴又和大家见面啦&a…

【Rust】所有权

目录 所有权基本概念所有权介绍栈与堆变量作用域 字符串字符串字面值&#xff08;&str&#xff09;String 类型相互转换所有权 内存结构对比注意事项和常见坑使用场景 内存与分配变量与数据交互的方式&#xff08;一&#xff09;&#xff1a;移动变量与数据交互的方式&…

4月29日日记

终于是考完解析几何了&#xff0c;今天昨天突击了一下&#xff0c;感觉确实学会了很多之前不会的东西&#xff0c;但是可能距离高分还差很多。这次考试不太理想。大部分原因是前期没学&#xff0c;吸取教训&#xff0c;早点开始复习微积分。明天还有一节微积分&#xff0c;但是…

【深度对比】Google Play与IOS 马甲包处理差异分析

在移动应用发布与推广过程中&#xff0c;马甲包&#xff08;Cloned App / Alternate Version&#xff09; 曾被广泛用于流量测试、风险隔离、多品牌运营等场景中。随着 Google Play 与 Apple App Store 审核政策不断收紧&#xff0c;开发者们越来越关注两个平台对“马甲包”的态…

MCP 架构全解析:Host、Client 与 Server 的协同机制

目录 &#x1f3d7;️ MCP 架构全解析&#xff1a;Host、Client 与 Server 的协同机制 &#x1f4cc; 引言 &#x1f9e9; 核心架构组件 1. Host&#xff08;主机&#xff09; 2. Client&#xff08;客户端&#xff09; 3. Server&#xff08;服务器&#xff09; &#…

记录一次无界微前端的简单使用

记录一次无界微前端使用 无界微前端主应用子应用nginx配置 无界微前端 https://wujie-micro.github.io/doc/ 因为使用的是vue项目主应用和次应用都是 所以用的封装的。 https://wujie-micro.github.io/doc/pack/ 主应用 安装 选择对应的版本 # vue2 框架 npm i wujie-vue2…

LLM应用于自动驾驶方向相关论文整理(大模型在自动驾驶方向的相关研究)

1、《HILM-D: Towards High-Resolution Understanding in Multimodal Large Language Models for Autonomous Driving》 2023年9月发表的大模型做自动驾驶的论文&#xff0c;来自香港科技大学和人华为诺亚实验室&#xff08;代码开源&#xff09;。 论文简介&#xff1a; 本文…

FTP-网络文件服务器

部署思路 单纯上传下载ftp系统集成间的共享 samba网络存储服务器 NFS 网络文件服务器&#xff1a;通过网络共享文件或文件夹&#xff0c;实现数据共享 NAS &#xff08; network append storage):共享的是文件夹 FTP&#xff1a;文件服务器samba&#xff1a;不同系统间的文件…

在 Ubuntu 22.04 x64 系统安装/卸载 1Panel 面板

一、 1Panel 是什么&#xff1f; 1Panel 是一款基于 Go 语言开发的现代化开源服务器管理面板&#xff08;类似宝塔面板&#xff09;&#xff0c;专注于容器化&#xff08;Docker&#xff09;和云原生环境管理&#xff0c;提供可视化界面简化服务器运维操作。 1. 1Panel主要功…

Redis | Redis集群模式技术原理介绍

关注&#xff1a;CodingTechWork Redis 集群模式概述 Redis 集群&#xff08;Cluster&#xff09;模式是 Redis 官方提供的分布式解决方案&#xff0c;旨在解决单机 Redis 在数据量和性能上的限制。它通过数据分片、高可用性和自动故障转移等特性&#xff0c;提供了水平扩展和…

Servlet小结

视频链接&#xff1a;黑马servlet视频全套视频教程&#xff0c;快速入门servlet原理servlet实战 什么是Servlet&#xff1f; 菜鸟教程&#xff1a;Java Servlet servlet&#xff1a; server applet Servlet是一个运行在Web服务器&#xff08;如Tomcat、Jetty&#xff09;或应用…

数据库进阶之MySQL 程序

1.目标 1> 了解mysqlId服务端程序 2> 掌握mysql客户端程序的使用 3> 了解工具包中的其他程序 2. MySQL程序简介 本章介绍 MySQL 命令⾏程序以及在运⾏这些程序时指定选项的⼀般语法(如:mysql -uroot -p)。 对常⽤程序进⾏详细的讲解(实用工具的使用方法)&#xf…

VS2022 设置 Qt Project Settings方法

本文解决的问题&#xff1a;创建完成后&#xff0c;如需要用到Sql或者Socket等技术&#xff0c;需要设置Qt Project Settings&#xff1b; 1、打开VS2022编译器&#xff0c;创建QT项目工程 2、创建完成后&#xff0c;点击 解决方案 →右键属性 3、选择 Qt Project Settings →…

React:封装一个评论回复组件

分析 用户想要一个能够显示评论列表&#xff0c;并且允许用户进行回复的组件。可能还需要支持多级回复&#xff0c;也就是对回复进行再回复。然后&#xff0c;我要考虑组件的结构和功能。 首先&#xff0c;数据结构方面&#xff0c;评论应该包含id、内容、作者、时间&#xf…