Java比较无equals/hashCode类型集合如何正确校验元素基数
问题描述
- 场景:使用未定义
equals、hashCode方法的第三方类型,已自定义单元素相等判断规则:实例的data属性值一致则判定两实例相等 - 缺陷:现有集合比较逻辑未统计元素出现次数(基数),会将
[Box(1),Box(1),Box(2)]和[Box(1),Box(2),Box(2)]错误判定为相等,对应单元测试断言失败。
根因分析
原有集合比较逻辑遍历第一个集合的元素时,只要在第二个集合中找到任意一个匹配项就判定匹配,不会标记或移除已经被匹配过的元素,导致多个重复元素会重复匹配到第二个集合中的同一个元素,无法校验元素的出现次数。
修正实现
由于第三方类未实现hashCode方法,无法基于哈希结构做元素频次统计,因此采用拷贝集合+匹配移除的方案,替换原有集合重载的equals方法即可:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.function.BiPredicate; static <T> boolean equals(Collection<T> collection1, Collection<T> collection2, BiPredicate<T, T> equals) { if (collection1.size() != collection2.size()) { return false; } // 拷贝待匹配集合,避免修改原始入参产生副作用 List<T> pendingMatch = new ArrayList<>(collection2); for (T t1 : collection1) { Iterator<T> matchIterator = pendingMatch.iterator(); boolean isMatched = false; while (matchIterator.hasNext()) { T t2 = matchIterator.next(); if (equals.test(t1, t2)) { // 匹配成功后移除该元素,防止后续元素重复匹配 matchIterator.remove(); isMatched = true; break; } } if (!isMatched) { return false; } } return pendingMatch.isEmpty(); }
逻辑验证
针对原失败测试用例[Box(1), Box(1), Box(2)]和[Box(1), Box(2), Box(2)]:
- 初始待匹配列表为
[Box(1), Box(2), Box(2)] - 遍历第一个集合的第一个
Box(1),匹配到待匹配列表中的Box(1)并移除,待匹配列表变为[Box(2), Box(2)] - 遍历第一个集合的第二个
Box(1),遍历待匹配列表无对应匹配项,直接返回false,符合断言预期。
所有原有测试用例均可正常通过。
内容的提问来源于stack exchange,提问作者Jónás Balázs
相关产品推荐
相关产品推荐

