Java TreeSet如何实现按id去重且按name字段排序
问题根因
TreeSet判断元素重复的逻辑和HashSet完全不同:HashSet依赖equals()+hashCode()判断重复,而TreeSet完全基于比较器(Comparable的compareTo方法或者传入的Comparator)的返回值判断重复,只要返回0就视为相同元素,不会存入。你当前的compareTo仅比对name字段,只要两个对象name不同,哪怕id完全一致也会被判定为不同元素存入,所以你重写的equals和hashCode在TreeSet场景下完全没生效。
解决方案
方案1:修改Member类的compareTo方法(推荐,符合类的自然排序定义)
调整比较逻辑:先判断id是否相等,如果相等直接返回0(判定为重复,不存入);如果id不等,再按name升序排序。修改后的compareTo代码如下:
@Override public int compareTo(Member m) { // 先判断id是否重复,重复直接返回0视为相同元素 if(this.id == m.getID()){ return 0; } // id不重复的情况下按name升序排序 return this.name.compareTo(m.getName()); }
改完后运行测试类,id重复的元素会被自动拦截,最终输出无重复id,且按name升序排列。
方案2:给TreeSet传入自定义Comparator(无需修改原有Member类,适合不能修改实体类的场景)
如果你不想改动原有Member类的代码,可以在初始化TreeSet的时候直接传入自定义比较器,逻辑和上述一致:
// Java 8 以下写法 TreeSet<Member> ts = new TreeSet<>(new Comparator<Member>() { @Override public int compare(Member o1, Member o2) { if(o1.getID() == o2.getID()){ return 0; } return o1.getName().compareTo(o2.getName()); } }); // Java 8+ 可以简化为Lambda写法 // TreeSet<Member> ts = new TreeSet<>((o1, o2) -> o1.getID() == o2.getID() ? 0 : o1.getName().compareTo(o2.getName()));
注意事项
- 如果使用方案1,建议同步调整
equals()和hashCode()的逻辑和compareTo保持一致,避免类在HashSet/HashMap等场景下的行为和TreeSet不一致,符合Java规范要求。 - TreeSet默认遇到重复元素会丢弃新存入的、保留先存入的元素,如果需要调整重复时的保留逻辑,可以在比较器里扩展对应规则。
内容的提问来源于stack exchange,提问作者HSF
相关产品推荐
相关产品推荐

