Java 8按ID过滤列表并做属性差值运算存入新列表报错解决
Java 8 实现两个列表按ID匹配并计算差值的正确方案
原始数据与需求
原始列表定义
List<Manipulate> a = new ArrayList<>(); a.add(new Manipulate(1,100)); a.add(new Manipulate(2,200)); List<Manipulate> b = new ArrayList<>(); b.add(new Manipulate(1,10)); b.add(new Manipulate(2,20));
需求
仅使用Java 8,按ID匹配两个列表,将列表a中对象的quantity属性减去列表b中对应ID对象的quantity属性,结果存入新的Manipulate对象列表c。
原代码的问题
原代码报错的直接原因是:stream().map()返回的是Stream<Manipulate>,未转换为List就直接赋值给List<Manipulate>类型变量,导致类型不匹配。除此之外还有两个隐性问题:
- 直接修改原列表a中的对象属性,会污染原始数据,不符合"存入新列表"的需求;
- 嵌套遍历两个列表,数据量大时性能低下;
- 使用
forEach处理匹配元素,若b中存在重复ID会多次修改数值,不符合ID唯一的常规场景。
正确实现方案
最优实现(性能优先)
先将列表b转换为以ID为键的Map,避免重复遍历,然后生成新的结果列表:
// 将列表b转为ID到Manipulate的映射,提升匹配效率 Map<Integer, Manipulate> bIdMap = b.stream() .collect(Collectors.toMap(Manipulate::getId, Function.identity())); // 生成结果列表c List<Manipulate> c = a.stream() .map(aItem -> { // 获取b中对应ID的对象 Manipulate bItem = bIdMap.get(aItem.getId()); // 计算差值,兼容b中无对应ID的情况(若需求仅保留匹配ID,可添加filter) int newQuantity = aItem.getQuantity() - (bItem != null ? bItem.getQuantity() : 0); // 创建新对象存储结果,不修改原始数据 return new Manipulate(aItem.getId(), newQuantity); }) .collect(Collectors.toList()); // 将Stream转换为List,解决类型不匹配问题
方案优势
- 时间复杂度从O(n*m)降至O(n+m),数据量大时性能提升明显;
- 创建新的
Manipulate对象,完全隔离原始数据与结果数据; - 兼容b中无对应ID的场景,若仅需保留a、b都存在的ID,可在
map前添加过滤:.filter(aItem -> bIdMap.containsKey(aItem.getId()))
修正原思路的实现(最小改动)
如果要沿用原代码的嵌套遍历思路,需修正类型转换与数据修改问题:
List<Manipulate> c = a.stream().map(aItem -> { // 查找b中对应ID的唯一对象(假设ID唯一) Optional<Manipulate> matchedBItem = b.stream() .filter(bItem -> bItem.getId() == aItem.getId()) .findFirst(); if (matchedBItem.isPresent()) { int newQuantity = aItem.getQuantity() - matchedBItem.get().getQuantity(); return new Manipulate(aItem.getId(), newQuantity); } // 无匹配时返回原数值的新对象(或根据需求调整) return new Manipulate(aItem.getId(), aItem.getQuantity()); }).collect(Collectors.toList()); // 必须调用collect转换为List
内容的提问来源于stack exchange,提问作者sathish kumar
相关产品推荐
相关产品推荐

