
本文介绍如何使用 java stream 高效、安全地对包含子列表的对象集合进行嵌套过滤,重点解决可变对象并发修改风险、parallelstream 过度开销及代码可维护性问题,并推荐面向对象封装与不可变设计的最佳实践。
本文介绍如何使用 java stream 高效、安全地对包含子列表的对象集合进行嵌套过滤,重点解决可变对象并发修改风险、parallelstream 过度开销及代码可维护性问题,并推荐面向对象封装与不可变设计的最佳实践。
在实际开发中,常遇到类似需求:一个 List
- 线程不安全:peek() 和 setListaLavorazioni() 属于副作用操作,在 parallelStream() 中直接修改共享可变对象,极易引发竞态条件或数据不一致;
- 性能反模式:对中小规模集合(如子列表平均
- 设计异味(Feature Envy):过滤逻辑散落在服务层,违背“行为应归属其数据所在类”的封装原则。
✅ 推荐方案:以不可变性为前提,将过滤逻辑内聚到 Attivita 类中,并返回新实例。首先增强 Attivita 类:
public class Attivita {
private List<lavorazione> listaLavorazioni;
// 其他字段与构造方法...
// ✅ 纯函数式方法:返回新 Attivita 实例,保持不可变语义
public Attivita filterLavorazioniById(Long idIstanza) {
List<lavorazione> filtered = listaLavorazioni.stream()
.filter(l -> Objects.nonNull(l.getIstanza())
&& Objects.equals(l.getIstanza().getId(), idIstanza))
.toList(); // Java 16+,兼容旧版可用 Collectors.toList()
// 使用 Builder 模式或复制构造器创建新实例(避免浅拷贝陷阱)
return new Attivita.Builder(this)
.withListaLavorazioni(filtered)
.build();
}
}</lavorazione></lavorazione>
调用端代码由此变得简洁、安全、可读性强:
@CachePut(value = "sdipp-cache")
public List<attivita> ottieniListaAttivita(String prefixCall, Long idIstanza) throws DefaultException {
return servizioGenerico.ottieniCall(prefixCall)
.getListaAttivita()
.stream() // ✅ 默认串行流,更轻量、更可控
.map(attivita -> attivita.filterLavorazioniById(idIstanza))
.toList(); // Java 16+;旧版用 .collect(Collectors.toList())
}</attivita>
⚠️ 关键注意事项:
- 勿滥用 parallelStream():除非父列表或任一子列表规模达万级且 CPU 密集,否则优先选用串行流;
- 强制不可变性:filterLavorazioniById() 必须返回新对象,而非修改 this——这是保障并发安全与函数式清晰性的基石;
- 空值防御:示例中已加入 Objects.nonNull(l.getIstanza()) 检查,生产环境务必覆盖所有潜在 null 路径;
- 缓存友好:因返回全新对象,不影响原有缓存实体,与 @CachePut 完全兼容。
总结:优雅 ≠ 复杂。真正的高性能与可维护性,源于职责内聚(把过滤逻辑放进 Attivita)、数据不可变(返回新实例)和流策略务实(小数据用串行)。这不仅是 Stream 的正确用法,更是面向对象与函数式思想的协同落地。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











