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

Java ArrayList去重问题求助:相同内容但嵌套列表顺序不同的Customer对象无法被去重

问题解决:忽略List元素顺序的对象去重

你的问题核心在于Java中普通List的equals()和hashCode()是依赖元素顺序的:两个内容完全一致但顺序不同的List会被判定为不相等,导致Customer对象的equals()返回false,最终HashSet无法识别它们是重复项。

下面提供两种可行的解决方案,你可以根据业务场景选择:

方案一:修改Customer类的equals()和hashCode(),忽略accPaList的顺序

这种方案从实体类本身入手,让Customer的相等性判断不依赖accPaList的元素顺序,适用于所有需要判断Customer相等的场景。

修改hashCode()方法

将accPaList的哈希值计算改为顺序无关的方式,比如对所有元素的哈希值求和:

@Override
public int hashCode(){
    final int prime=31;
    int result = 1;
    result = prime * result + ((abcAccount ==null)? 0 : abcAccount.hashCode());
    // 替换原accPaList.hashCode(),改为计算所有元素哈希值的和
    result = prime * result + ((accPaList ==null)? 0 : 
        accPaList.stream().mapToInt(Object::hashCode).sum());
    result = prime * result + ((id ==null)? 0 : id.hashCode());
    result = prime * result + ((startDate ==null)? 0 : startDate.hashCode());
    result = prime * result + ((type ==null)? 0 : type.hashCode());
    return result;
}

修改equals()方法

在比较accPaList时,先判断大小是否一致,再通过排序后比较来忽略顺序(如果accPaList允许重复元素,这种方式比转HashSet更准确,因为HashSet会自动去重):

@Override
public boolean equals(Object obj){
    if(this == obj) return true;
    if(obj == null) return false;
    if(getClass() != obj.getClass()) return false;
    Customer other = (Customer) obj;
    // 其他字段的比较保持不变
    if(abcAccount ==null){
        if(other.abcAccount !=null) return false;
    } else if (!abcAccount.equals(other.abcAccount)) return false;
    if(id ==null){
        if(other.id !=null) return false;
    } else if (!id.equals(other.id)) return false;
    if(startDate ==null){
        if(other.startDate !=null) return false;
    } else if (!startDate.equals(other.startDate)) return false;
    if(type ==null){
        if(other.type !=null) return false;
    } else if (!type.equals(other.type)) return false;

    // 重写accPaList的比较逻辑
    if (accPaList == null) {
        return other.accPaList == null;
    }
    if (other.accPaList == null) {
        return false;
    }
    if (accPaList.size() != other.accPaList.size()) {
        return false;
    }
    // 排序后比较两个List
    List<AccPaList> sortedThis = new ArrayList<>(accPaList);
    List<AccPaList> sortedOther = new ArrayList<>(other.accPaList);
    // 为AccPaList创建排序比较器(基于其所有字段)
    sortedThis.sort((a1, a2) -> {
        // 比较HeadAcc
        int headAccComp = compareGcAcc(a1.getHeadAcc(), a2.getHeadAcc());
        if (headAccComp != 0) return headAccComp;
        // 比较SouAcc
        int souAccComp = compareGcAcc(a1.getSouAcc(), a2.getSouAcc());
        if (souAccComp != 0) return souAccComp;
        // 比较ConcId
        return compareConcId(a1.getConcId(), a2.getConcId());
    });
    sortedOther.sort((a1, a2) -> {
        int headAccComp = compareGcAcc(a1.getHeadAcc(), a2.getHeadAcc());
        if (headAccComp != 0) return headAccComp;
        int souAccComp = compareGcAcc(a1.getSouAcc(), a2.getSouAcc());
        if (souAccComp != 0) return souAccComp;
        return compareConcId(a1.getConcId(), a2.getConcId());
    });
    return sortedThis.equals(sortedOther);
}

// 辅助方法:比较GCAcc对象
private int compareGcAcc(GCAcc acc1, GCAcc acc2) {
    if (acc1 == null && acc2 == null) return 0;
    if (acc1 == null) return -1;
    if (acc2 == null) return 1;
    int actComp = acc1.getAct().compareTo(acc2.getAct());
    if (actComp != 0) return actComp;
    return acc1.getAcctCurrency().compareTo(acc2.getAcctCurrency());
}

// 辅助方法:比较ConcId对象
private int compareConcId(ConcId id1, ConcId id2) {
    if (id1 == null && id2 == null) return 0;
    if (id1 == null) return -1;
    if (id2 == null) return 1;
    int idComp = id1.getId().compareTo(id2.getId());
    if (idComp != 0) return idComp;
    return id1.getVersion().compareTo(id2.getVersion());
}

修改完成后,原来的HashSet去重代码就能正常识别顺序不同但内容一致的Customer对象了。

方案二:使用自定义Comparator和TreeSet去重(不修改实体类)

如果你不想修改实体类的equals()和hashCode()(比如其他业务场景需要依赖List顺序的相等性判断),可以用TreeSet配合自定义比较器来实现去重:

CustomerResponse response = service.callGetDetails(id,name);
List<Customer> responseList = response.getCustomerList();
CustomerResponse response1 = service.callGetDetails(id,name);
responseList.addAll(response1.getCustomerList());

// 自定义Customer比较器,忽略accPaList的顺序
Comparator<Customer> customerComparator = (c1, c2) -> {
    // 比较基础字段:id、startDate、type
    int idComp = Objects.compare(c1.getId(), c2.getId(), Comparator.nullsFirst(String::compareTo));
    if (idComp != 0) return idComp;
    int dateComp = Objects.compare(c1.getStartDate(), c2.getStartDate(), Comparator.nullsFirst(String::compareTo));
    if (dateComp != 0) return dateComp;
    int typeComp = Objects.compare(c1.getType(), c2.getType(), Comparator.nullsFirst(String::compareTo));
    if (typeComp != 0) return typeComp;

    // 比较AbcAccount
    int abcAccComp = compareGcAcc(c1.getAbcAccount(), c2.getAbcAccount());
    if (abcAccComp != 0) return abcAccComp;

    // 比较accPaList,忽略顺序
    if (c1.getAccPaList() == null && c2.getAccPaList() == null) return 0;
    if (c1.getAccPaList() == null) return -1;
    if (c2.getAccPaList() == null) return 1;
    if (c1.getAccPaList().size() != c2.getAccPaList().size()) return Integer.compare(c1.getAccPaList().size(), c2.getAccPaList().size());

    // 排序后比较accPaList
    List<AccPaList> sorted1 = new ArrayList<>(c1.getAccPaList());
    List<AccPaList> sorted2 = new ArrayList<>(c2.getAccPaList());
    sorted1.sort((a1, a2) -> {
        int headComp = compareGcAcc(a1.getHeadAcc(), a2.getHeadAcc());
        if (headComp != 0) return headComp;
        int souComp = compareGcAcc(a1.getSouAcc(), a2.getSouAcc());
        if (souComp != 0) return souComp;
        return compareConcId(a1.getConcId(), a2.getConcId());
    });
    sorted2.sort((a1, a2) -> {
        int headComp = compareGcAcc(a1.getHeadAcc(), a2.getHeadAcc());
        if (headComp != 0) return headComp;
        int souComp = compareGcAcc(a1.getSouAcc(), a2.getSouAcc());
        if (souComp != 0) return souComp;
        return compareConcId(a1.getConcId(), a2.getConcId());
    });

    return sorted1.equals(sorted2) ? 0 : 1; // 相等返回0,TreeSet会合并重复项
};

// 使用TreeSet去重
Set<Customer> uniqueCustomers = new TreeSet<>(customerComparator);
uniqueCustomers.addAll(responseList);
responseList = new ArrayList<>(uniqueCustomers);

这里的compareGcAcc和compareConcId方法和方案一中的辅助方法一致,你可以把它们提取成工具方法复用。

方案选择建议

  • 如果所有业务场景都认为“accPaList顺序不同但内容一致的Customer是同一个对象”,优先选方案一,一劳永逸。
  • 如果只有当前去重场景需要忽略顺序,其他场景仍需依赖List顺序判断相等,选方案二更灵活,不会影响现有业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 11:08:10