Java中使用TreeSet存储自定义pair类的元素插入异常问题
TreeSet使用自定义Comparator时的元素唯一性判断问题
核心原因:TreeSet的元素唯一性由Comparator决定
当给TreeSet传入自定义Comparator时,TreeSet会完全依赖这个比较器的返回值判断元素是否重复——只要compare(a,b)返回0,就认为a和b是同一个元素,不会重复插入,完全忽略你重写的equals和hashCode方法。
第一种Comparator的问题分析
你第一个Comparator仅比较w字段:
public int compare(pair a , pair b){ return a.w-b.w; }
p1、p2、p3的w值都是3,所以:
- 插入p2时,TreeSet用该比较器和已存在的p1对比,返回0,判定为重复元素,拒绝插入
- 插入p3时,和p1对比同样返回0,也被判定为重复,拒绝插入
- 只有p4的
w是4,和p1对比返回非0值,正常插入
最终集合里只剩p1和p4,size为2。
修改后的Comparator为什么能正常插入
修改后的Comparator依次比较w、i、j三个字段:
public int compare(pair a , pair b){ if(a.w==b.w ){ if(a.i==b.i) return a.j-b.j; return a.i-b.i; } return a.w-b.w; }
此时p1、p2、p3之间的对比结果都不会是0:
- p1(1,3,3)和p2(1,2,3):w相同、i相同、j不同,返回3-2=1≠0
- p1和p3(2,2,3):w相同、i不同,返回1-2=-1≠0
- p2和p3:w相同、i不同,返回1-2=-1≠0
TreeSet判定这三个都是不同元素,加上p4,四个元素全部被插入。
纠正你的误解
HashSet这类基于哈希表的集合才会用equals和hashCode判断元素重复,但TreeSet是基于红黑树的有序集合,它的元素唯一性规则是:
- 如果元素实现
Comparable接口,用compareTo方法返回0判定相等 - 如果传入自定义Comparator,就用
compare方法返回0判定相等
此时equals和hashCode的实现不会影响TreeSet的插入逻辑(不过从Java规范来说,建议让equals的结果和比较器的判断一致,避免出现集合逻辑矛盾)。
额外代码问题提示
你的pair类里的hashCode方法定义错误,正确的hashCode方法应为无参,且需添加@Override注解:
@Override public int hashCode(){ return Objects.hash(i,j,w); }
这个错误不影响当前TreeSet的问题,但会导致将pair对象放入HashSet这类集合时出现异常。
内容的提问来源于stack exchange,提问作者luke__skywalkrrr
相关产品推荐
相关产品推荐

