Java中使用Stream API优化for循环
业务逻辑:计算总额
原来使用for的逻辑代码
// 计算总额
double total = 0.0;
for (RkDetail rkDetail: rkDetailList) {if (rkDetail.getTotal() != null) {total += rkDetail.getTotal();}
}
使用Java 8 Stream API实现,使代码更简洁且易读
// 计算总额
double total = rkDetailList.stream().filter(r -> r.getTotal() != null).mapToDouble(RkDetail::getTotal).sum();
让我们一步步拆解这段代码的含义,用生活中快递分拣的场景帮助你理解:
double total = rkDetailList.stream() // 【开启传送带】.filter(r -> r.getTotal() != null) // 【安检仪-过滤空包裹】.mapToDouble(RkDetail::getTotal) // 【拆包取物】.sum(); // 【自动累加】
1. 开启传送带 rkDetailList.stream()
想象你有一个装满快递包裹的货架(rkDetailList
),现在要启动一条传送带处理这些包裹。
stream()
就像启动传送带,让包裹(列表中的每个 RkDetail
对象)依次进入处理流程。
📦📦📦📦 包裹们排着队进入传送带...
2. 安检仪过滤 filter(r -> r.getTotal() != null)
传送带上的第一个设备是安检仪,它会检查每个包裹:
r -> r.getTotal() != null
表示 "只允许装有金额的包裹通过"
(这里的 r
代表每个快递包裹,getTotal()
查看包裹内的金额单)
✅ 有金额单的包裹通过
❌ 空包裹(金额单为null)被丢弃
📦✅ -> 金额100元
📦❌ -> 金额单丢失
📦✅ -> 金额50元
📦✅ -> 金额200元
3. 拆包取物 mapToDouble(RkDetail::getTotal)
通过安检的包裹来到拆包工位,这里的工作人员会:
RkDetail::getTotal
表示 "拆开包裹,只取出里面的金额单"
mapToDouble
将金额单上的数字转换为计算机能直接计算的数值(double类型)
输入:📦✅ -> 输出:100.0
输入:📦✅ -> 输出:50.0
输入:📦✅ -> 输出:200.0
4. 自动累加 sum()
最后来到自动累加器,它会将所有金额单上的数值相加:
sum()
自动完成 100.0 + 50.0 + 200.0 = 350.0
💰 最终总额:350.0
完整流程示意图
原始包裹队列 → [传送带] → [安检过滤] → [拆包取数] → [自动求和](List) (stream()) (filter) (mapToDouble) (sum)
重点记忆口诀
-
流式处理三步走:取流 → 过滤 → 转换
-
箭头符号看数据:
->
左边是输入,右边是处理逻辑 -
双冒号是快捷方式:
类名::方法名
等价于x -> x.方法名()
实际案例演示
假设原始数据:
RkDetail 包裹1 = new RkDetail(100.0); RkDetail 包裹2 = new RkDetail(null); // 空包裹 RkDetail 包裹3 = new RkDetail(50.0); List<RkDetail> rkDetailList = Arrays.asList(包裹1, 包裹2, 包裹3);
代码执行过程:
-
包裹2 在
filter
阶段被过滤 -
包裹1 和 包裹3 的金额被提取
-
计算 100.0 + 50.0 = 150.0
最终 total
的值为 150.0
通过这种生活化的类比,是不是更容易理解Stream流的工作原理了呢?在实际开发中,这种处理方式比传统for循环更直观,尤其适合处理复杂的数据流水线操作。
优化点说明:
-
Stream API 替代显式循环
-
通过流式操作
stream()
+filter()
+mapToDouble()
+sum()
链式调用,消除手动循环和中间变量,代码更简洁 -
逻辑表达更声明式(描述做什么,而非如何做)
-
-
空值过滤更直观
-
filter(r -> r.getTotal() != null)
直接体现空值过滤逻辑,一目了然
-
-
避免中间状态变量
-
原始代码需要维护
total
变量和循环体,流式操作直接通过无状态计算得到结果
-
扩展优化建议(视场景选用):
-
并行流加速
java
复制
下载
// 仅当列表极大且线程安全时使用 double total = rkDetailList.parallelStream().filter(r -> r.getTotal() != null).mapToDouble(RkDetail::getTotal).sum();
-
空集合防御
java
复制
下载
// 如果 rkDetailList 可能为 null(根据业务场景决定是否添加) List<RkDetail> safeList = Optional.ofNullable(rkDetailList).orElse(Collections.emptyList()); double total = safeList.stream()... // 后续操作同上
-
方法引用(若允许空值)
java
复制
下载
// 如果 RkDetail::getTotal 不可能返回 null(注意与当前场景冲突) double total = rkDetailList.stream().mapToDouble(RkDetail::getTotal).sum();
最终选择建议:
若运行环境是Java 8+且列表规模可控,推荐使用基础优化版本,兼顾简洁性与可维护性。