使用显式Comparator构造TreeSet时lower()方法返回结果不符合预期
问题原因
你自定义的Comparator违反了Java Comparator 接口的强制规范,导致TreeSet内部红黑树结构紊乱,最终查询结果错误:
Comparator接口明确要求compare(a, b)必须满足两个规则:- a大于b时返回正整数,a小于b时返回负整数,a等于b时返回0
compare(a, b)和compare(b, a)的符号必须相反,除非两者返回0
- 你写的比较逻辑在a等于b时会返回1,完全不满足上述要求,这就导致TreeSet在插入、排序元素时逻辑混乱,调用
lower()查询时自然返回错误结果。你遇到的返回值等于参数的问题,就是比较逻辑不符合规范的典型表现。
解决方法
分两种场景处理:
场景1:不需要存储重复元素
直接补全相等判断即可,修复后的比较器完全符合规范,lower()方法会正常返回预期的9:
TreeSet<Integer> tree = new TreeSet<Integer>(new Comparator<Integer>(){ @Override public int compare(Integer a, Integer b){ return Integer.compare(a, b); // 也可以手动写分支:a > b返回1,a < b返回-1,相等返回0 } });
场景2:确实需要存储值重复的元素
TreeSet本身是去重集合,原生不支持存储重复元素。如果需要强行存入值相等、逻辑上视为不同的元素,需要新增额外的区分维度,避免比较器在值相等时返回0,同时保证比较逻辑符合规范:
比如封装自定义元素,加入插入序号作为次要排序键:
import java.util.*; import java.util.concurrent.atomic.AtomicLong; // 自定义可存重复值的元素类 class RepeatableElement { private final int value; private final long seq; // 插入序号,相同值的元素序号不同 public RepeatableElement(int value, long seq) { this.value = value; this.seq = seq; } public int getValue() { return value; } public static void main(String[] args) { // 原子序号生成器,保证每个元素序号唯一 AtomicLong seqGenerator = new AtomicLong(0); TreeSet<RepeatableElement> tree = new TreeSet<>((a, b) -> { int valCmp = Integer.compare(a.value, b.value); if (valCmp != 0) { return valCmp; } // 值相等时用序号比较,永远不返回0,即可存入重复值 return Long.compare(a.seq, b.seq); }); // 插入元素时传入自动生成的序号 tree.add(new RepeatableElement(9, seqGenerator.getAndIncrement())); tree.add(new RepeatableElement(5, seqGenerator.getAndIncrement())); tree.add(new RepeatableElement(8, seqGenerator.getAndIncrement())); tree.add(new RepeatableElement(1, seqGenerator.getAndIncrement())); tree.add(new RepeatableElement(11, seqGenerator.getAndIncrement())); tree.add(new RepeatableElement(3, seqGenerator.getAndIncrement())); // 查询时构造临时元素传参即可 System.out.println(tree.lower(new RepeatableElement(11, Long.MAX_VALUE)).getValue()); } }
内容的提问来源于stack exchange,提问作者ram gopal
相关产品推荐
相关产品推荐

