基于二分搜索实现的contains方法无法正确检测重复元素?
嘿,我来帮你揪出问题的根源,顺便给你个更省心的解决方案!
问题排查与修复
你的自定义去重逻辑里藏着两个关键问题,才导致了元素误判和重复残留的情况:
1. 二分搜索的结果判断完全搞反了
Collections.binarySearch的返回规则可不像你想的那样简单,它的返回值逻辑是:
- 找到元素时:返回该元素在有序列表里的非负索引
- 没找到元素时:返回
-(插入位置) - 1(这个插入位置是指把新元素插进去还能保持列表有序的位置)
你现在的判断代码是:
if (index == -1) { return false; } else { return true; }
这就会导致:
- 只有当元素不存在且刚好要插在列表最开头时(返回-1),才会判定为“不存在”
- 其他所有情况(不管元素真的存在,还是不存在但插在别的位置)都会被判定为“存在”,这直接让大量不存在的元素被错误拦截,而本该拦截的重复元素却可能漏过。
正确的判断逻辑应该是直接检查索引是否大于等于0:
return index >= 0;
2. 列表只排了一次序,添加元素后就乱了
你只在最开始对列表做了一次快速排序,但每次往里面加新元素后,列表的有序性就被破坏了!而二分搜索的前提是列表必须严格按照指定的Comparator保持有序,不然搜索结果完全是瞎蒙的。
比如你排序后是[A,B,C],加个D还好,要是加个B,列表就变成[A,B,C,B],这时候再用二分搜索找元素,结果肯定错得离谱,这也是为什么你的列表里还会有重复元素的核心原因。
更省心的替代方案:用TreeSet自动搞定去重+排序
与其自己手动折腾排序和二分搜索,不如直接用Java集合框架里的TreeSet,它天生就是干这个的:
- 会按照你指定的Comparator自动保持元素有序
- 自动去重,重复元素加不进去
- 查找和添加的效率都是O(log n),比你手动实现的靠谱多了
给你个示例代码:
// 先定义好卡片的比较器 Comparator<Card> cardComparator = (card1, card2) -> card1.getName().compareTo(card2.getName()); // 初始化TreeSet,把比较器传进去 Set<Card> uniqueCardSet = new TreeSet<>(cardComparator); // 直接加元素就行,重复的会自动被过滤 uniqueCardSet.add(new Card("TheCard")); uniqueCardSet.add(new Card("TheOtherCard")); uniqueCardSet.add(new Card("TheCard")); // 这个重复的加不进去,不用自己判断 // 如果最后需要List类型,直接转一下就行 List<Card> uniqueCardList = new ArrayList<>(uniqueCardSet);
这样既不用自己写容易出错的判断逻辑,也能保证效率和正确性,完美解决你的问题~
内容的提问来源于stack exchange,提问作者Josh Hunter
相关产品推荐
相关产品推荐

