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

Java Stream API:让业务数据处理更优雅

在 Java 业务开发中,我们经常需要对集合数据进行**筛选(filter)、转换(map)、聚合(collect)**等操作。比如从一批结果中过滤出符合条件的记录,就像这样:

假数据

// 原始数据集合
List<Entity> entityList = Arrays.asList(new Entity(1L, "数据1", 200L),new Entity(2L, "数据2", 200L),new Entity(3L, "数据3", 300L),new Entity(4L, "数据4", 200L)
);
Long targetId = 200L; // 目标关联ID
idnameassociateId
1L数据 1200L
2L数据 2200L
3L数据 3300L
4L数据 4200L

操作代码

List<Entity> resultItemList = entityList.stream().filter(dataItem -> targetId.equals(dataItem.getAssociateId())).collect(Collectors.toList());

结果:包含 associateId 为 200L 的 3 条数据(ID 为 1、2、4)
filter:满足条件的对象才会被保留;
dataItem :代表 entityList 集合中的每一个 Entity 对象;
collect(Collectors.toList()) :将筛选后的结果收集到新的List中;
这种基于 Stream API 的处理方式以声明式的语法,让代码更简洁、意图更清晰。下面我们结合实际业务场景,探讨 Stream API 的更多实用写法。

提取关键信息:从对象集合到字段集合

在处理用户数据时,经常需要提取某个字段形成新集合。例如从用户列表中提取所有 ID,用于后续的批量查询:

假数据

List<User> userList = Arrays.asList(new User(101L, "张三", 25),new User(102L, "李四", 30),new User(103L, "王五", 28)
);
idnameage
101L张三25
102L李四30
103L王五28

操作代码

List<Long> userIdList = userList.stream().map(User::getId).collect(Collectors.toList());

结果[101, 102, 103]

这种操作在权限校验、关联查询等场景中极为常见。通过map方法可以轻松实现对象到字段的转换,避免了手动迭代的繁琐。

多条件筛选:精准定位目标数据

业务系统中,数据筛选往往需要满足多重条件。比如筛选 “已激活且余额大于 0 的 VIP 用户”:

假数据

List<User> userList = Arrays.asList(new User(101L, "张三", UserStatus.ACTIVATED, 500.0, true),new User(102L, "李四", UserStatus.LOCKED, 1000.0, true),new User(103L, "王五", UserStatus.ACTIVATED, -50.0, true),new User(104L, "赵六", UserStatus.ACTIVATED, 800.0, false)
);
idnamestatusbalanceisVip
101L张三ACTIVATED500.0true
102L李四LOCKED1000.0true
103L王五ACTIVATED-50.0true
104L赵六ACTIVATED800.0false

操作代码

List<User> qualifiedUsers = userList.stream().filter(user -> UserStatus.ACTIVATED.equals(user.getStatus())).filter(user -> user.getBalance() > 0).filter(user -> user.isVip()).collect(Collectors.toList());

结果:仅包含用户 “张三”(ID=101)

通过链式调用filter方法逐步缩小范围,最终仅保留同时满足以下条件的用户,相比嵌套的 if 语句,这种写法更易于理解和维护。

分组聚合:数据归类的高效方式

数据分组是统计分析的基础操作。按部门 ID 分组统计员工列表:

假数据

List<User> userList = Arrays.asList(new User(101L, "张三", 1L), // 部门ID=1new User(102L, "李四", 2L), // 部门ID=2new User(103L, "王五", 1L), // 部门ID=1new User(104L, "赵六", 3L)  // 部门ID=3
);
idnamedeptId
101L张三1L
102L李四2L
103L王五1L
104L赵六3L

操作代码

Map<Long, List<User>> deptUserMap = userList.stream().collect(Collectors.groupingBy(User::getDeptId));

结果

{1L: [User{id=101, name='张三', deptId=1},User{id=103, name='王五', deptId=1}],2L: [User{id=102, name='李四', deptId=2}],3L: [User{id=104, name='赵六', deptId=3}]
}

如果需要进一步统计数量,比如统计每个订单状态的数量:

假数据

List<Order> orderList = Arrays.asList(new Order(1L, OrderStatus.PAID),new Order(2L, OrderStatus.PAID),new Order(3L, OrderStatus.CANCELLED),new Order(4L, OrderStatus.PENDING)
);
idstatus
1LPAID
2LPAID
3LCANCELLED
4LPENDING

操作代码

Map<OrderStatus, Long> statusCountMap = orderList.stream().collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));

结果

{PAID: 2,CANCELLED: 1,PENDING: 1
}

这种分组统计在生成报表、数据看板等场景中应用广泛。

映射转换:构建快速查询结构

将列表转换为 Map 是提升查询效率的常用手段。例如将用户列表转为 “用户 ID - 用户对象” 的映射:

假数据

List<User> userList = Arrays.asList(new User(101L, "张三"),new User(102L, "李四"),new User(101L, "张三(重复ID)") // 模拟重复ID
);
idname
101L张三
102L李四
101L张三(重复 ID)

操作代码

Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(),(existing, replacement) -> replacement // 重复时保留新值));

结果

{101L: User(101, "张三(重复ID)"),102L: User(102, "李四")
}

Function.identity():静态方法,表示将流中的元素(User对象)本身作为Map的值。
通过指定 key 生成规则和冲突处理策略,可以轻松构建高效的查询结构,特别适合需要频繁根据 ID 查询对象的场景。

排序与限制:获取 topN 数据

业务中常需要获取排名靠前的数据,如销量最高的前 10 个商品:

假数据

List<Product> productList = Arrays.asList(new Product(1L, "手机", 1500L),new Product(2L, "电脑", 800L),new Product(3L, "平板", 2000L),new Product(4L, "手表", 1200L)
);
idnamesales
1L手机1500L
2L电脑800L
3L平板2000L
4L手表1200L

操作代码

List<Product> top10Products = productList.stream().sorted(Comparator.comparingLong(Product::getSales).reversed()).limit(2) // 简化示例,取前2.collect(Collectors.toList());

结果:按销量倒序排列的前 2 条数据(平板、手机)

sorted(Comparator.comparingLong(Product::getSales).reversed()) 通过Comparator.comparingLong方法创建比较器,按Product对象的getSales()方法返回值(long类型)进行排序。
reversed()表示降序排列(从高到低)。

sorted方法支持自定义排序规则,配合limit可以快速实现 topN 查询,避免了手动排序的复杂逻辑。

数值计算:聚合操作的便捷实现

对数值型字段进行聚合计算是业务统计的常见需求。例如计算所有订单的总金额:

假数据

List<Order> orderList = Arrays.asList(new Order(1L, new BigDecimal("199.99")),new Order(2L, new BigDecimal("299.50")),new Order(3L, new BigDecimal("500.00"))
);
idamount
1L199.99
2L299.50
3L500.00

操作代码

BigDecimal totalAmount = orderList.stream().map(Order::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

结果999.49
.reduce(BigDecimal.ZERO, BigDecimal::add):对流中的BigDecimal值进行累加求和。BigDecimal.ZERO是初始值,BigDecimal::add是累加操作【如果orderList为空,reduce会直接返回初始值BigDecimal.ZERO,不会抛出异常。】;
reduce方法提供了灵活的聚合能力,除了求和,还可以实现求最大值、最小值等操作,满足多样化的统计需求。

类型转换:DTO 与 VO 的优雅映射

在分层架构中,经常需要在 DTO 和 VO 之间进行转换。例如将符合条件的订单 DTO 转换为 VO:

假数据

List<OrderDTO> orderDTOList = Arrays.asList(new OrderDTO("ORD001", "张三", new BigDecimal("800")),new OrderDTO("ORD002", "李四", new BigDecimal("1200")),new OrderDTO("ORD003", "王五", new BigDecimal("1500"))
);
orderNobuyerNameamount
ORD001张三800
ORD002李四1200
ORD003王五1500

操作代码

List<OrderVO> orderVOList = orderDTOList.stream().filter(dto -> dto.getAmount().compareTo(new BigDecimal("1000")) > 0).map(dto -> {OrderVO vo = new OrderVO();vo.setOrderNo(dto.getOrderNo());vo.setUserName(dto.getBuyerName());vo.setTotalAmount(dto.getAmount());return vo;}).collect(Collectors.toList());

结果:包含 2 条数据(ORD002、ORD003),已转换为 OrderVO 类型

结合过滤和映射操作,可以在转换过程中同时完成数据清洗,使代码更加紧凑高效。

Stream API 的这些操作并非孤立存在,实际业务中常常需要组合使用。例如先过滤不符合条件的数据,再进行分组统计;或者先排序,再取前 N 条进行类型转换。这种流水线式的处理方式,不仅使代码结构清晰,更能直观地体现业务逻辑,大大提升了开发效率和代码可维护性。

完整代码

我用夸克网盘给你分享了「stream流配套代码」,链接:https://pan.quark.cn/s/9b16328cef08
下面这个是主程序,其余实体类文件在网盘里
在这里插入图片描述

package stream;import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;public class StreamDemoAll {public static void main(String[] args) {System.out.println("===== Java Stream API 示例 ======");// 1. 筛选操作 - 从Entity集合中筛选出associateId为200L的数据System.out.println("\n1. 筛选操作:");List<Entity> entityList = Arrays.asList(new Entity(1L, "数据1", 200L),new Entity(2L, "数据2", 200L),new Entity(3L, "数据3", 300L),new Entity(4L, "数据4", 200L));Long targetId = 200L;List<Entity> resultItemList = entityList.stream().filter(dataItem -> targetId.equals(dataItem.getAssociateId())).collect(Collectors.toList());System.out.println("筛选结果: " + resultItemList);resultItemList.forEach(item -> System.out.println("ID: " + item.getId() + ", 名称: " + item.getName() + ", 关联ID: " + item.getAssociateId()));// 2. 提取关键信息 - 从User集合中提取所有IDSystem.out.println("\n2. 提取关键信息:");List<User> userList1 = Arrays.asList(new User(101L, "张三", 25),new User(102L, "李四", 30),new User(103L, "王五", 28));List<Long> userIdList = userList1.stream().map(User::getId).collect(Collectors.toList());System.out.println("用户ID列表: " + userIdList);// 3. 多条件筛选 - 筛选已激活且余额大于0的VIP用户System.out.println("\n3. 多条件筛选:");List<User> userList2 = Arrays.asList(new User(101L, "张三", UserStatus.ACTIVATED, 500.0, true),new User(102L, "李四", UserStatus.LOCKED, 1000.0, true),new User(103L, "王五", UserStatus.ACTIVATED, -50.0, true),new User(104L, "赵六", UserStatus.ACTIVATED, 800.0, false));List<User> qualifiedUsers = userList2.stream().filter(user -> UserStatus.ACTIVATED.equals(user.getStatus())).filter(user -> user.getBalance() > 0).filter(user -> user.getVip()).collect(Collectors.toList());System.out.println("符合条件的用户: " + qualifiedUsers);// 4. 分组聚合 - 按部门ID分组统计员工列表System.out.println("\n4. 分组聚合:");List<User> userList3 = Arrays.asList(new User(101L, "张三", 1L),new User(102L, "李四", 2L),new User(103L, "王五", 1L),new User(104L, "赵六", 3L));Map<Long, List<User>> deptUserMap = userList3.stream().collect(Collectors.groupingBy(User::getDeptId));System.out.println("按部门分组的用户: ");deptUserMap.forEach((deptId, users) -> {System.out.println("部门 " + deptId + ": " + users);});// 5. 统计数量 - 统计每个订单状态的数量System.out.println("\n5. 统计数量:");List<Order> orderList1 = Arrays.asList(new Order(1L, OrderStatus.PAID),new Order(2L, OrderStatus.PAID),new Order(3L, OrderStatus.CANCELLED),new Order(4L, OrderStatus.PENDING));Map<OrderStatus, Long> statusCountMap = orderList1.stream().collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));System.out.println("订单状态统计: ");statusCountMap.forEach((status, count) -> {System.out.println(status + ": " + count);});// 6. 映射转换 - 将用户列表转为ID-用户对象映射System.out.println("\n6. 映射转换:");List<User> userList4 = Arrays.asList(new User(101L, "张三"),new User(102L, "李四"),new User(101L, "张三(重复ID)") // 模拟重复ID);Map<Long, User> userMap = userList4.stream().collect(Collectors.toMap(User::getId,Function.identity(),(existing, replacement) -> replacement // 重复时保留新值));System.out.println("用户ID-对象映射: ");userMap.forEach((id, user) -> {System.out.println(id + ": " + user);});// 7. 排序与限制 - 获取销量最高的前2个商品System.out.println("\n7. 排序与限制:");List<Product> productList = Arrays.asList(new Product(1L, "手机", 1500L),new Product(2L, "电脑", 800L),new Product(3L, "平板", 2000L),new Product(4L, "手表", 1200L));List<Product> topProducts = productList.stream().sorted(Comparator.comparingLong(Product::getSales).reversed()).limit(2).collect(Collectors.toList());System.out.println("销量最高的前2个商品: " + topProducts);// 8. 数值计算 - 计算所有订单的总金额System.out.println("\n8. 数值计算:");List<Order> orderList2 = Arrays.asList(new Order(1L, new BigDecimal("199.99")),new Order(2L, new BigDecimal("299.50")),new Order(3L, new BigDecimal("500.00")));BigDecimal totalAmount = orderList2.stream().map(Order::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);System.out.println("订单总金额: " + totalAmount);// 9. 类型转换 - DTO与VO的优雅映射System.out.println("\n9. 类型转换:");List<OrderDTO> orderDTOList = Arrays.asList(new OrderDTO("ORD001", "张三", new BigDecimal("800")),new OrderDTO("ORD002", "李四", new BigDecimal("1200")),new OrderDTO("ORD003", "王五", new BigDecimal("1500")));List<OrderVO> orderVOList = orderDTOList.stream().filter(dto -> dto.getAmount().compareTo(new BigDecimal("1000")) > 0).map(dto -> {OrderVO vo = new OrderVO();vo.setOrderNo(dto.getOrderNo());vo.setUserName(dto.getBuyerName());vo.setTotalAmount(dto.getAmount());return vo;}).collect(Collectors.toList());System.out.println("转换后的订单VO列表: " + orderVOList);System.out.println("\n===== 示例结束 ======");}
}
===== Java Stream API 示例 ======1. 筛选操作:
筛选结果: [stream.Entity@270421f5, stream.Entity@52d455b8, stream.Entity@4f4a7090]
ID: 1, 名称: 数据1, 关联ID: 200
ID: 2, 名称: 数据2, 关联ID: 200
ID: 4, 名称: 数据4, 关联ID: 2002. 提取关键信息:
用户ID列表: [101, 102, 103]3. 多条件筛选:
符合条件的用户: [User{id=101, name='张三', age=null, status=ACTIVATED, balance=500.0, isVip=true, deptId=null}]4. 分组聚合:
按部门分组的用户: 
部门 1: [User{id=101, name='张三', age=null, status=null, balance=null, isVip=null, deptId=1}, User{id=103, name='王五', age=null, status=null, balance=null, isVip=null, deptId=1}]
部门 2: [User{id=102, name='李四', age=null, status=null, balance=null, isVip=null, deptId=2}]
部门 3: [User{id=104, name='赵六', age=null, status=null, balance=null, isVip=null, deptId=3}]5. 统计数量:
订单状态统计: 
PENDING: 1
PAID: 2
CANCELLED: 16. 映射转换:
用户ID-对象映射: 
101: User{id=101, name='张三(重复ID)', age=null, status=null, balance=null, isVip=null, deptId=null}
102: User{id=102, name='李四', age=null, status=null, balance=null, isVip=null, deptId=null}7. 排序与限制:
销量最高的前2个商品: [Product{id=3, name='平板', sales=2000}, Product{id=1, name='手机', sales=1500}]8. 数值计算:
订单总金额: 999.499. 类型转换:
转换后的订单VO列表: [OrderVO{orderNo='ORD002', userName='李四', totalAmount=1200}, OrderVO{orderNo='ORD003', userName='王五', totalAmount=1500}]===== 示例结束 ======
http://www.dtcms.com/a/331454.html

相关文章:

  • 【120页PPT】人工智能与数字化转型的业财融合(附下载方式)
  • TDengine IDMP 基本功能(6. 无问智推)
  • Web攻防-业务逻辑篇APP隐私合规资源处理违规收集拒绝服务在线检测项目工具
  • 深度剖析!体育数据 API 及电竞数据 API 在体育电竞领域的核心地位
  • java动态代理机制I(初稿)
  • 职得AI简历-免费AI简历生成工具
  • C++ 排序指南
  • 7、C 语言数组进阶知识点总结
  • 解决SQL Server连接失败:Connection refused: connect
  • 力扣(LeetCode) ——225 用队列实现栈(C语言)
  • C++中的回调函数
  • C++中的内存管理(一)
  • BitDock——让你的Windows桌面变为Mac
  • 【ai写代码】lua-判断表是否被修改
  • Mysql基本使用语句(一)
  • [激光原理与应用-271]:理论 - 波动光学 - 电磁波谱,光是一种可视化的电磁波
  • 广义矩估计随机近似中1.2和2.1的差异
  • 获取iframe中canvas画面
  • 爬虫数据存储全攻略:从 Robots 协议到文件存储
  • C++11新特性深度解析
  • Linux软件下载菜单脚本
  • Effective C++ 条款41:理解隐式接口和编译期多态
  • 系统设计——DDD领域模型驱动实践
  • 深入浅出词向量(Word2Vec):从理论到实践
  • 数据结构初阶(13)排序算法-选择排序(选择排序、堆排序)(动图演示)
  • 【Java 后端】Spring Boot 集成 JPA 全攻略
  • HTTPS 工作原理
  • 电池充放电测试仪厂家:技术深耕与场景驱动的行业进阶
  • Java使用Apache POI读取Excel文件
  • Vue浅学