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
相关产品推荐
相关产品推荐

