如何用单个Java Stream构建含原列表元素及字段修改副本的新列表
单次流式处理的实现方案
可通过单次遍历原列表完成全部逻辑,根据对元素顺序的要求,可选择适配不同JDK版本的实现,无需多次触发流式调用:
Java 8 兼容的通用实现
借助flatMap操作,将每个原列表元素映射为包含两个对象的子流——一个是原条目本身,一个是修改指定字段后的副本,最后将所有子流合并为最终列表即可,全程只遍历一次原列表:
List<SomeObject> newList = list.stream() .flatMap(l -> { // 基于原对象实例构造字段修改后的副本 SomeObject modifiedObj = new SomeObject(l); modifiedObj.setField1("New Value"); // 返回包含两个元素的子流,逐元素完成两路输出 return Stream.of(l, modifiedObj); }) .collect(Collectors.toList());
注意:该方案生成的新列表元素顺序为「原列表第1项、第1项的修改副本、原列表第2项、第2项的修改副本……」,和你原有代码「所有原条目在前、所有修改后条目在后」的排列顺序有差异,若对列表元素顺序没有严格要求,可直接使用该方案。
严格匹配原顺序的高版本JDK实现
如果必须保持「全部原条目排在列表前半段、全部修改后副本排在列表后半段」的顺序,且JDK版本在12及以上,可借助Collectors.teeing()在单次遍历中并行完成两路收集,最后合并结果:
List<SomeObject> newList = list.stream() .collect( Collectors.teeing( // 第一路收集:保留所有原条目 Collectors.toList(), // 第二路收集:生成并收集所有字段修改后的副本 Collectors.mapping(l -> { SomeObject modifiedObj = new SomeObject(l); modifiedObj.setField1("New Value"); return modifiedObj; }, Collectors.toList()), // 合并两路收集结果,严格匹配原有代码的元素顺序 (originalItems, modifiedItems) -> { List<SomeObject> combined = new ArrayList<>(originalItems.size() + modifiedItems.size()); combined.addAll(originalItems); combined.addAll(modifiedItems); return combined; } ) );
性能对比
两类方案都仅对原列表做一次线性遍历,相比你原有两次stream的实现,减少了一次循环遍历的开销,也省去了两次临时列表的中间生成步骤,运行效率和内存占用都更优。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

