compareTo方法是否有任何约束命令将对象放入标准java TreeSet(即实现为红黑树的集合)?我有
public class RuleTuple implements Comparable {
String head;
String[] rhs;
public String toString() {
StringBuffer b = new StringBuffer();
b.append(head+":");
for( String t: rhs )
b.append(" "+t);
return b.toString();
}
public int compareTo(Object obj) {
RuleTuple src = (RuleTuple)obj;
int cmp = head.compareTo(src.head);
if( cmp!=0 )
return cmp;
if( rhs.length != src.rhs.length )
return rhs.length - src.rhs.length;
for( int i=0; i<rhs.length; i++ )
if( rhs[i].compareTo(src.rhs[i]) != 0 )
return rhs[i].compareTo(src.rhs[i]);
return 0;
}
...
}
我认为任何将对象映射到线性顺序的方法都可以,只要它符合偏序标准:反身性,不对称性和传递性。其中只有传递性并不是立即显而易见的,但在我看来,如果通过排序标准比较对象,则传递性如下。 (我首先比较标题,如果相同,则比较rhs的长度,如果相同,则比较数组的元素。)
显然,RuleTuple.compareTo()方法不一致,因为当删除“test:test [22,33)”时,它按以下顺序遍历树:
test[22,33): 'HAVING' condition <-- comparison#1
test: test[4,19) group_by_clause <-- comparison#2
test: model_clause <-- comparison#3
test: group_by_clause
test:
test: test[22,33)
test: group_by_clause test[22,33) <-- comparison#4; wrong branch!
test: test[4,19) <-- comparison#5
test: group_by_clause model_clause
...
test: test[4,19) group_by_clause model_clause
...
test[4,19): test[5,8) test[8,11)
...
因此,它无法找到并删除树上的对象。我的直觉是否正确?
答案 0 :(得分:6)
Comparator和Comparable的另一个(通常被忽略的)条件是“临时一致性”,即比较两个对象的结果不应该改变,只要它们在TreeMap(或您使用的任何其他结构)中用作键。比较器/可比较,如用于Collections.binarySearch的排序数组,或由最小堆实现的PriorityQueue - 即使对于Arrays.sort,也不应在排序完成之前更改元素。
这实际上意味着你的密钥不应该改变,至少不会改变顺序。
原因是TreeMap假定二叉树的节点总是以正确的顺序 - 只有这样才能在O(log(n))
而不是O(n)
中进行查找和更改。< / p>
如果您需要更改密钥,则应首先将其从结构中删除,然后进行更改,然后重新添加。
(顺便说一下,对于像HashMap这样的基于散列的结构中的equals
和hashCode
密钥也是如此。)
作为额外的奖励,这里是代码的泛型使用变体:
public class RuleTuple implements Comparable<RuleTuple> {
String head;
String[] rhs;
public String toString() {
StringBuilder b = new StringBuilder();
b.append(head+":");
for( String t: rhs )
b.append(" "+t);
return b.toString();
}
public int compareTo(RuleTuple src) {
int cmp = head.compareTo(src.head);
if( cmp!=0 )
return cmp;
if( rhs.length != src.rhs.length )
return rhs.length - src.rhs.length;
for( int i=0; i<rhs.length; i++ ) {
int diff = rhs[i].compareTo(src.rhs[i]);
if(diff != 0)
return diff;
}
return 0;
}
...
}
我还将StringBuffer更改为StringBuilder(这里不使用同步更有效)并且在循环中只使用了一个compareTo
。您还可以通过在此处为每个toString
运算符使用两个附加内容来优化+
方法。