
本文介绍如何使用 Java 8 Stream 处理 List
本文介绍如何使用 java 8 stream 处理 `list>` 类型数据,针对相同字符串 id 的两组元素,按首次出现顺序保留第一个值、减去第二个值,并维持原始 id 出现顺序,最终生成差值结果列表。
在实际开发中,我们常遇到结构扁平但语义嵌套的数据,例如 List> 中每个子列表形如 [id, value],且约束条件明确:每个 ID 恰好出现两次,第二次的 value 需从第一次中减去,结果需保持 ID 首次出现的顺序。虽然问题看似适合 Stream 流式处理,但需注意:过度追求“函数式”可能牺牲可读性与可维护性;而合理结合 Stream 与状态映射(如 LinkedHashMap)则能兼顾逻辑清晰与代码简洁。
以下提供两种推荐方案:
✅ 推荐方案一:Stream + LinkedHashMap(兼顾顺序、简洁与可读性)
import java.util.*;
import java.util.stream.Collectors;
List<list>> input = Arrays.asList(
Arrays.asList("1", 100),
Arrays.asList("2", 200),
Arrays.asList("5", 300),
Arrays.asList("1", 10),
Arrays.asList("2", 20),
Arrays.asList("5", 40)
);
// 使用 LinkedHashMap 维持插入顺序,同时记录首次值
Map<string integer> acc = new LinkedHashMap();
input.forEach(item -> {
String id = (String) item.get(0);
int value = (Integer) item.get(1);
acc.merge(id, value, (first, second) -> first - second); // 第一次 put,第二次执行 first - second
});
// 转为最终 List<list>>
List<list>> result = acc.entrySet().stream()
.map(entry -> Arrays.asList(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
System.out.println(result); // [[1, 90], [2, 180], [5, 260]] ← 注意:此处按题意应为 [1,90],[2,180],[5,260];原文示例 [[1,90],[2,80],[5,240]] 存在计算笔误(200−20=180,非80;300−40=260,非240)</list></list></string></list>
⚠️ 注意:原问题示例结果 ["2",80] 和 ["5",240] 与输入数据不符(200−20=180,300−40=260),本文以真实运算逻辑为准。若业务要求“第一次值减第二次值”,则 merge 的 remapping function 应为 (first, second) -> first - second,完全符合题设“第二个减去第一个”的表述(即 first − second)。
❌ 不推荐:纯 Stream 嵌套遍历(低效且易错)
如问题中尝试的 parallelStream().map(...forEach...) 写法存在多重问题:
- forEach 在 map 内部无返回值,无法参与流链;
- 并行流嵌套导致竞态与不可预测顺序;
- 时间复杂度 O(n²),远不如单次遍历的 O(n) 高效。
因此,避免在 Stream 中滥用副作用操作(如 forEach 修改外部变量)或嵌套流遍历。
✅ 进阶提示:类型安全重构建议
为提升健壮性,建议将原始 List> 封装为 POJO:
record Entry(String id, int value) {}
List<entry> typedInput = input.stream()
.map(lst -> new Entry((String) lst.get(0), (Integer) lst.get(1)))
.collect(Collectors.toList());</entry>
后续所有操作均可基于 Entry 进行,消除强制类型转换风险,也更利于单元测试与扩展。
总结:面对“分组+有序减法”类需求,优先选择 Stream.collectingAndThen + LinkedHashMap::merge 组合,既满足函数式风格,又保障性能、顺序与可维护性;切勿为用 Stream 而牺牲代码本质——清晰的逻辑永远比炫技更重要。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











