Java中比较两个TestFoo对象列表的异常问题求助
需要判断existingItems和newItems两个TestFoo类型的列表内容是否完全一致。当前做法是将两个列表转换为SortedSet,使用基于uniqueItemDetailId属性的Comparator排序后调用.equals()比较,但即使列表内容不同,比较结果仍为相等,未触发预期的if逻辑。
当前代码:
Comparator<TestFoo> budgetItemComparator = Comparator.comparing(TestFoo::getUniqueItemDetailId); SortedSet<TestFoo> sortedExistingESItems = new TreeSet<>(itemComparator); sortedExistingESItems.addAll(existingESItems); SortedSet<TestFoo> sortedNewESItems = new TreeSet<>(budgetItemComparator); sortedNewESItems.addAll(newItems); if (!sortedExistingESItems.equals(sortedNewESItems)) { finalSavingTestFooItems.addAll(newItems); testFooRepository.deleteAll(existingESItems); }
TestFoo类定义:
public class TestFoo { @Id private String id; @Field(type = Keyword) private String uniqueId; @Field(type = Keyword) private String uniqueIdDetailId; @Field(type = Keyword) private String uniqueIdSyncGuId; @Field(type = Keyword, index = false) private String name; @Field(type = Keyword) private String yearId; @Field(type = Keyword) private Set<String> types; @Field(type = Keyword) private Set<String> managers; @Field(type = Nested, includeInParent = true) private Set<TestHigh> hierarchies; @Field(type = Nested, includeInParent = true) private Set<TestTeam> teamMembers; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TestFoo that = (TestFoo) o; return Objects.equals(uniqueId, that.uniqueId) && Objects.equals(uniqueIdDetailId, that.uniqueIdDetailId) && Objects.equals(uniqueIdSyncGuId, that.uniqueIdSyncGuId) && Objects.equals(name, that.name) && Objects.equals(yearId, that.yearId) && Objects.equals(types, that.types) && Objects.equals(managers, that.managers) && Objects.equals(hierarchies, that.hierarchies) && Objects.equals(teamMembers, that.teamMembers); } @Override public int hashCode() { return Objects.hash(uniqueId, uniqueIdDetailId, uniqueIdSyncGuId, name, yearId, types, managers, hierarchies, teamMembers); } }
TreeSet(属于SortedSet)的元素相等判断逻辑完全依赖构造时传入的Comparator:只要两个元素通过Comparator.compare()返回0,就会被视为同一个元素,完全不会调用对象自身的equals()方法。
你的代码中Comparator仅根据uniqueIdDetailId比较,而TestFoo的equals()是比较所有属性。这就导致:即使两个TestFoo对象的uniqueIdDetailId相同,但其他属性(比如name、types)不同,TreeSet也会把它们当成同一个元素处理——添加时后者会覆盖前者,最终两个SortedSet会被判定为相等,但实际原列表的内容是不同的。
另外代码中存在变量名笔误:创建sortedExistingESItems时用了itemComparator,但定义的变量是budgetItemComparator,不过这不是核心问题。
根据需求(判断两个列表的内容是否完全一致,包括所有属性),提供几种可行方案:
方案1:修正Comparator,使其与equals逻辑一致
让Comparator的比较逻辑和TestFoo的equals()、hashCode()保持一致,这样TreeSet的元素判断才会符合预期:
Comparator<TestFoo> testFooComparator = Comparator .comparing(TestFoo::getUniqueId) .thenComparing(TestFoo::getUniqueIdDetailId) .thenComparing(TestFoo::getUniqueIdSyncGuId) .thenComparing(TestFoo::getName) .thenComparing(TestFoo::getYearId) .thenComparing(TestFoo::getTypes) .thenComparing(TestFoo::getManagers) .thenComparing(TestFoo::getHierarchies) .thenComparing(TestFoo::getTeamMembers); SortedSet<TestFoo> sortedExisting = new TreeSet<>(testFooComparator); sortedExisting.addAll(existingESItems); SortedSet<TestFoo> sortedNew = new TreeSet<>(testFooComparator); sortedNew.addAll(newItems); if (!sortedExisting.equals(sortedNew)) { finalSavingTestFooItems.addAll(newItems); testFooRepository.deleteAll(existingESItems); }
注意:如果TestHigh和TestTeam没有正确实现equals()和hashCode(),需要先完善这两个类的对应方法,否则集合比较会出错。
方案2:不使用SortedSet,直接排序后比较列表
如果不需要去重,只是比较两个列表的元素(不考虑顺序)是否完全一致,可以先对两个列表排序,再直接比较列表:
// 用和equals一致的Comparator排序 existingESItems.sort(testFooComparator); newItems.sort(testFooComparator); if (!existingESItems.equals(newItems)) { finalSavingTestFooItems.addAll(newItems); testFooRepository.deleteAll(existingESItems); }
这种方法保留原列表的重复元素(如果有的话),更准确反映原列表的内容差异。
方案3:使用HashSet(需注意重复元素)
如果你的列表中没有重复元素,也可以直接转换为HashSet比较,因为HashSet的equals()依赖对象自身的equals()和hashCode():
Set<TestFoo> existingSet = new HashSet<>(existingESItems); Set<TestFoo> newSet = new HashSet<>(newItems); if (!existingSet.equals(newSet)) { finalSavingTestFooItems.addAll(newItems); testFooRepository.deleteAll(existingESItems); }
注意:如果原列表有重复元素,HashSet会自动去重,无法判断重复次数的差异,此时不适合用这个方案。
内容的提问来源于stack exchange,提问作者Anandharaj Anandharaj

