You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java8中基于nbr字段合并对象列表的重复值异常问题

Java 8 基于nbr字段合并两个列表时重复值问题解决

问题场景

有两个从BigQuery查询得到的Data DTO列表:

BQ结果列表1(bq1)

List<Data> bq1 = {
  [name:"abc", country: "ca", nbr:334],
  [name:"dcc", country: "ca", nbr:222], 
  [name:"aop", country: "in", nbr:234]
}

BQ结果列表2(bq2)

List<Data> bq2 = {
  [color:a,  nbr:334],
  [color:n, nbr:334], 
  [color:e, nbr:234]
}

期望基于nbr字段合并两个列表,得到如下结果:

List<Data> result = {
  [name:"aop", country: "in", nbr:234, color: e], 
  [name:"abc", country: "ca", nbr:334, color: a], 
  [name:"abc", country: "ca", nbr:334, color: n]
}

但实际得到错误结果,同nbr的记录出现重复的color值:

List<Data> result = {
  [name:"aop", country: "in", nbr:234, color: e], 
  [name:"abc", country: "ca", nbr:334, color: a], 
  [name:"abc", country: "ca", nbr:334, color: a]
}

当前使用Java 8的实现代码:

List<Data> resultList = new ArrayList<>();

Map<String, List<Data>> mapByItemNumber = bqList.stream()
    .collect(Collectors.groupingBy(Data::getNbr));

for (Data item : bq3List) {

    if (mapByItemNumber.containsKey(item.getNbr())) {

        for (Data i : mapByItemNumber.get(item.getNbr())) {

            i.setColor(item.getColor());  
            resultList.add(i);
        }
    }
}

问题原因

代码直接修改了mapByItemNumber中存储的原Data对象实例,并将同一个实例多次添加到结果列表:

  • 处理bq2中第一个nbr=334的条目(color:a)时,修改了bq1中name="abc"的Data对象的color为a,并添加到结果列表;
  • 处理bq2中第二个nbr=334的条目(color:n)时,再次修改同一个Data实例的color为n,此时结果列表中已存在的该实例也会被同步修改;
  • 若后续操作或打印时出现异常,会导致最终结果出现重复的错误值。

解决方案

创建新的Data对象实例进行合并,避免修改原对象和重复引用:

方案1:使用构造函数复制属性

假设Data类有包含所有属性的构造函数:

List<Data> resultList = new ArrayList<>();

// 注意:确保getNbr的返回类型与map的key类型一致,若nbr是Integer则用Map<Integer, List<Data>>
Map<Object, List<Data>> mapByItemNumber = bq1.stream()
    .collect(Collectors.groupingBy(Data::getNbr));

for (Data item : bq2) {
    List<Data> matchedItems = mapByItemNumber.get(item.getNbr());
    if (matchedItems != null) {
        for (Data original : matchedItems) {
            // 创建新的Data对象,复制原属性并设置color
            Data merged = new Data(original.getName(), original.getCountry(), original.getNbr(), item.getColor());
            resultList.add(merged);
        }
    }
}

方案2:使用Builder模式

如果Data类有Builder实现:

List<Data> resultList = new ArrayList<>();

Map<Object, List<Data>> mapByItemNumber = bq1.stream()
    .collect(Collectors.groupingBy(Data::getNbr));

for (Data item : bq2) {
    List<Data> matchedItems = mapByItemNumber.get(item.getNbr());
    if (matchedItems != null) {
        for (Data original : matchedItems) {
            Data merged = Data.builder()
                .name(original.getName())
                .country(original.getCountry())
                .nbr(original.getNbr())
                .color(item.getColor())
                .build();
            resultList.add(merged);
        }
    }
}

方案3:使用Stream API简化实现

Map<Object, List<Data>> mapByItemNumber = bq1.stream()
    .collect(Collectors.groupingBy(Data::getNbr));

List<Data> resultList = bq2.stream()
    .flatMap(item -> mapByItemNumber.getOrDefault(item.getNbr(), Collections.emptyList())
        .stream()
        .map(original -> {
            // 手动复制属性创建新对象
            Data merged = new Data();
            merged.setName(original.getName());
            merged.setCountry(original.getCountry());
            merged.setNbr(original.getNbr());
            merged.setColor(item.getColor());
            return merged;
        }))
    .collect(Collectors.toList());

关键注意点

  • 确保mapByItemNumber的key类型与item.getNbr()的返回类型一致(比如都是Integer或String),避免因类型不匹配导致的匹配失败;
  • 始终通过创建新对象来存储合并结果,避免修改原列表中的对象,防止引发其他未知问题;
  • 若Data类是不可变类(没有setter方法),则必须通过构造函数或Builder来创建新实例。

内容的提问来源于stack exchange,提问作者neha menezes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 18:42:35