首页/开发/代码质量与评审速查/手法:Replace Loop with Pipeline(管道取代循环)
重构手法62 条

手法:Replace Loop with Pipeline(管道取代循环)

用 map/filter/reduce 等管道操作表达集合处理:过滤、转换、聚合各自成段,意图比手工循环更直接;管道链过长时切分为命名阶段。

Replace Loop with Pipeline 是什么

用 filter、map、reduce 等管道操作表达集合处理:过滤、转换、聚合各自成一段,意图比手工循环更直接,也不再需要索引变量与临时结果集合。它来自函数式编程的集合操作传统,在 Java Stream、C# LINQ、JavaScript 数组方法、Python 生成式里都有对应形态。

基本信息

形态filter → map → collect / reduce
收益声明式表达意图,减少索引与中间变量
注意调试断点与短路语义和循环不同
语言对应Java Stream、JS 数组方法、Python 推导式、C# LINQ

适用判断

  • 循环体里只做「挑出满足条件的、转换成另一种形状、收集」这三件事
  • 循环里出现索引变量但只用来取值,没有真正的下标运算
  • 同一次遍历里既有过滤又有映射,逻辑叠在一起难读
  • 需要在集合运算中途观察结果,用中间集合变量逐个打印
  • 集合处理的每一步都可以命名:找出中国区办公室、取城市、排序

循环与管道对照

循环与管道对照
// 循环版:意图被索引与临时集合盖住
List<String> names = new ArrayList<>();
for (Office o : offices) {
    if (o.isChina()) names.add(o.city());
}

// 管道版:三步各自可读
List<String> chinaCities = offices.stream()
    .filter(Office::isChina)
    .map(Office::city)
    .sorted()
    .toList();

// 管道过长时按阶段命名,别靠注释切段
List<String> topCities = chinaCities.stream().limit(10).toList();

循环与管道对比

维度手工循环管道
表达方式命令式,描述怎么做声明式,描述要什么
中间变量需要结果集合通常不需要
调试断点直观,可逐步查看需要中间 peek 或拆开管道
短路行为自己写 breakfindFirst 等操作自带短路
性能可控制一次遍历注意多次遍历与装箱开销
管道里塞多行 lambda 会让可读性反而不如循环。经验做法是:一步只做一件事,超过两三行就提取为命名方法,管道本身保持成「动词短语的序列」。
短路语义要核对:流上的 findFirst、anyMatch 在中途即可结束,与循环里的 break 类似但更隐式;而 sorted 这类有状态操作会强制遍历全部元素。改动集合大小时,行为差异可能在数据量上来后才暴露。

易错点

  • 管道里塞多行 lambda:可读性反而不如循环,调试也更费劲
  • 忽略副作用与短路语义差异:findFirst 等操作的行为需核对,惰性求值容易被误判为不执行
  • 为每个小循环都改管道:简单循环被拆成多段短管道,反而更难一眼看懂
  • 重复遍历同一集合:链式操作各自遍历一遍,大集合上开销明显

数据来源与参考

  • 《重构》第 2 版 · 第 8 章(Replace Loop with Pipeline)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌