如何比较含多列表属性的POJO/DTO对象?忽略顺序且无需排序
解决方案:不排序、不拆解对象实现列表无序相等判断
当然可以做到!不用手动拆解Boo对象,也不用给列表排序,核心思路是通过元素频率统计来判断两个列表是否包含完全相同的元素(忽略顺序)。下面给你两种可行的实现方式:
1. 手动实现:自定义equals方法(无第三方依赖)
如果你不想引入额外库,可以直接在Boo类里重写equals和hashCode,用哈希表统计元素出现次数来做判断:
import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; class Boo { private List<Foo> foos; private List<Integer> pointers; // 构造器、getter、setter自行补充 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Boo boo = (Boo) o; // 分别校验两个列表的无序相等性 return isUnorderedEqual(this.foos, boo.foos) && isUnorderedEqual(this.pointers, boo.pointers); } // 通用工具方法:判断两个列表元素相同(忽略顺序) private <T> boolean isUnorderedEqual(List<T> list1, List<T> list2) { if (list1 == list2) return true; if (list1 == null || list2 == null || list1.size() != list2.size()) return false; Map<T, Integer> freqMap = new HashMap<>(); // 统计第一个列表的元素频率 for (T item : list1) { freqMap.put(item, freqMap.getOrDefault(item, 0) + 1); } // 用第二个列表抵消频率 for (T item : list2) { Integer count = freqMap.get(item); if (count == null || count == 0) { // 元素不存在或频率已耗尽,直接返回不等 return false; } freqMap.put(item, count - 1); } // 所有元素频率都归零则相等 return freqMap.values().stream().allMatch(c -> c == 0); } @Override public int hashCode() { // 注意:equals重写后必须同步重写hashCode,这里用基于元素hash的累加值(顺序不影响结果) return Objects.hash(calculateUnorderedHash(foos), calculateUnorderedHash(pointers)); } private <T> int calculateUnorderedHash(List<T> list) { if (list == null) return 0; int hash = 0; for (T item : list) { hash += item != null ? item.hashCode() : 0; } return hash; } }
⚠️ 注意:Foo类必须正确重写equals和hashCode方法,否则无法准确判断元素是否相等。
2. 借助Guava库:用Multiset简化实现
如果你的项目已经引入了Guava库,用Multiset可以让代码更简洁——它本质就是专门用来统计元素出现次数的集合,两个Multiset相等的条件就是元素完全相同且出现次数一致:
import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import java.util.List; import java.util.Objects; class Boo { private List<Foo> foos; private List<Integer> pointers; // 构造器、getter、setter自行补充 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Boo boo = (Boo) o; // 直接用Multiset的equals方法判断无序相等 return Multiset.copyOf(this.foos).equals(Multiset.copyOf(boo.foos)) && Multiset.copyOf(this.pointers).equals(Multiset.copyOf(boo.pointers)); } @Override public int hashCode() { return Objects.hash(Multiset.copyOf(foos), Multiset.copyOf(pointers)); } }
这种方式完全不用自己写频率统计逻辑,代码更简洁易维护。
额外提醒
- 两种实现都考虑了
null列表和null元素的情况,可以直接使用。 - 重写
equals时一定要同步重写hashCode,否则会导致HashMap、HashSet等集合的行为异常。
内容的提问来源于stack exchange,提问作者Deepesh kumar Gupta
相关产品推荐
相关产品推荐

