You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何合并含List<XYZ>成员的Java对象A?实现去重与覆盖

嘿,这个问题我之前做项目的时候刚好碰到过,给你分享几种实用的实现思路,完全贴合你的需求~

实现思路与方案

首先得明确核心需求:

  • 基础字段(idresponse):若两个对象的字段内容不同,用新对象(或指定优先级的对象)的值覆盖;相同则覆盖也没变化。
  • 集合字段(slots):合并两个集合为无重复元素的并集,同时如果XYZ内部还有嵌套集合,也要考虑递归合并的场景。

1. 手动实现合并(最灵活可控)

这种方式能完全按照你的业务逻辑定制,适合复杂场景。

第一步:定义XYZ的“重复”规则

要对slots去重,必须先让XYZ类能判断两个实例是否为“重复元素”。通常我们会基于业务唯一标识重写equals()hashCode()方法,比如假设XYZ有一个唯一ID字段xyzId

class XYZ {
    private String xyzId;
    // 其他字段 + 嵌套集合(比如List<SomeOtherClass> nestedList)

    // 重写equals和hashCode,基于xyzId判断唯一性
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        XYZ xyz = (XYZ) o;
        return Objects.equals(xyzId, xyz.xyzId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(xyzId);
    }

    // getter、setter、构造器省略
}

第二步:实现A类的合并方法

可以在A类里写两种合并方式:一种是修改原对象,另一种是返回新的合并对象(推荐,避免副作用)。

方式一:返回新的合并对象(无副作用)

class A {
    String id;
    String response;
    List<XYZ> slots;

    // 将当前对象与another合并,返回全新的A对象
    public A mergeIntoNew(A another) {
        A merged = new A();
        // 初始化基础字段为当前对象的值
        merged.id = this.id;
        merged.response = this.response;
        merged.slots = this.slots != null ? new ArrayList<>(this.slots) : new ArrayList<>();

        if (another == null) {
            return merged;
        }

        // 覆盖基础字段:如果another的字段非空,就替换当前值(符合“不同则更新”)
        merged.id = Optional.ofNullable(another.id).orElse(merged.id);
        merged.response = Optional.ofNullable(another.response).orElse(merged.response);

        // 合并slots并去重
        if (another.slots != null) {
            Set<XYZ> slotSet = new HashSet<>(merged.slots);
            slotSet.addAll(another.slots);
            merged.slots = new ArrayList<>(slotSet);
        }

        return merged;
    }

    // getter、setter、构造器省略
}

方式二:处理XYZ内部的嵌套集合合并

如果你的XYZ类里还有嵌套集合,需要递归合并内部内容(比如两个相同xyzIdXYZ,要合并它们的内部集合),可以调整合并逻辑:

public A mergeIntoNew(A another) {
    A merged = new A();
    // 基础字段初始化...

    if (another == null) {
        return merged;
    }

    // 覆盖基础字段...

    // 处理slots:不仅去重,还要合并相同XYZ的内部集合
    if (another.slots != null) {
        // 把当前slots转成Map,方便快速查找相同xyzId的元素
        Map<String, XYZ> currentSlotMap = merged.slots.stream()
                .collect(Collectors.toMap(XYZ::getXyzId, xyz -> xyz));

        for (XYZ anotherXyz : another.slots) {
            String xyzId = anotherXyz.getXyzId();
            if (currentSlotMap.containsKey(xyzId)) {
                // 已存在相同XYZ,递归合并内部字段和集合
                currentSlotMap.get(xyzId).merge(anotherXyz);
            } else {
                // 不存在,直接添加
                currentSlotMap.put(xyzId, anotherXyz);
            }
        }

        // 把Map转回List
        merged.slots = new ArrayList<>(currentSlotMap.values());
    }

    return merged;
}

对应的,XYZ类也要添加merge方法:

public XYZ merge(XYZ another) {
    if (another == null) return this;
    // 合并内部嵌套集合(示例)
    if (another.nestedList != null) {
        if (this.nestedList == null) {
            this.nestedList = new ArrayList<>();
        }
        Set<SomeOtherClass> nestedSet = new HashSet<>(this.nestedList);
        nestedSet.addAll(another.nestedList);
        this.nestedList = new ArrayList<>(nestedSet);
    }
    // 其他字段的合并逻辑...
    return this;
}

2. 借助工具类简化基础字段处理

如果你的类有很多基础字段,手动复制太麻烦,可以用工具类(比如Apache Commons BeanUtils)快速处理基础字段的覆盖,再手动处理集合:

// 示例:用BeanUtils复制基础字段,然后处理集合
A merged = new A();
// 先复制obj1的字段
BeanUtils.copyProperties(merged, obj1);
// 再用obj2的字段覆盖,符合“不同则更新”
BeanUtils.copyProperties(merged, obj2);

// 手动合并slots并去重
Set<XYZ> slotSet = new HashSet<>();
if (obj1.getSlots() != null) slotSet.addAll(obj1.getSlots());
if (obj2.getSlots() != null) slotSet.addAll(obj2.getSlots());
merged.setSlots(new ArrayList<>(slotSet));

注意:BeanUtils默认会用null值覆盖原有值,如果不想让null覆盖,需要自定义转换器。

3. 用MapStruct生成合并代码(适合多类场景)

如果你的项目中有很多类需要合并逻辑,用MapStruct可以在编译期生成高效的映射代码,减少重复工作:

@Mapper
public interface AMapper {
    AMapper INSTANCE = Mappers.getMapper(AMapper.class);

    // 映射基础字段,忽略slots(手动处理)
    @Mapping(target = "slots", ignore = true)
    A mergeBaseFields(A target, A source);

    // 自定义合并逻辑
    default A merge(A obj1, A obj2) {
        A merged = mergeBaseFields(obj1, obj2);
        // 合并slots并去重
        Set<XYZ> slotSet = new HashSet<>();
        if (obj1.getSlots() != null) slotSet.addAll(obj1.getSlots());
        if (obj2.getSlots() != null) slotSet.addAll(obj2.getSlots());
        merged.setSlots(new ArrayList<>(slotSet));
        return merged;
    }
}

使用时直接调用:

A merged = AMapper.INSTANCE.merge(obj1, obj2);
总结
  • 简单场景:手动实现merge方法,完全可控;
  • 多类合并场景:用MapStruct减少重复代码;
  • 快速处理基础字段:结合BeanUtils+手动集合处理;
  • 核心前提:必须明确“重复元素”的定义(重写equals/hashCode或用唯一ID映射),否则无法正确去重。

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

火山引擎 最新活动