虽然文档说一致性不是强制性的,但最好始终确保这种一致性,因为您永远不知道您的对象是否有一天会出现在 TreeMap / TreeSet 或类似的地方。如果compareTo() 为 2 个不相等的对象返回 0,则所有基于 Tree 的集合都被破坏。
例如,想象一个类 Query,实现一个 SQL 查询,有 2 个字段:
- tableList:表格列表
- 参考:使用此类查询的程序列表
如果两个对象的 tableList 相等,则假设它们相等,即 tableList 是该对象的自然键。 hashCode() 和equals() 只考虑字段tableList:
public class Query implements Comparable {
List<String> tableList;
List<String> references;
Query(List<String> tableList, List<String> references) {
this.tableList = tableList;
this.references = references;
Collections.sort(tableList); // normalize
}
@Override
public int hashCode() {
int hash = 5;
hash = 53 * hash + Objects.hashCode(this.tableList);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Query other = (Query) obj;
return Objects.equals(this.tableList, other.tableList);
}
}
假设我们希望按照引用的数量进行排序。
天真地编写代码会产生一个compareTo() 方法,如下所示:
public int compareTo(Object o) {
Query other = (Query) o;
int s1 = references.size();
int s2 = other.references.size();
if (s1 == s2) {
return 0;
}
return s1 - s2;
}
这样做似乎没问题,因为相等和排序是在两个单独的字段上完成的,到目前为止一切都很好。
但是,无论何时放入TreeSet 或TreeMap,都是灾难性的:这些类的实现认为如果compareTo 返回0,则元素相等。在这种情况下,这意味着每个具有相同引用数量的对象确实是“相等”的对象,显然情况并非如此。
更好的compareTo() 方法可能是:
public int compareTo(Object o) {
Query other = (Query) o;
// important to match equals!!!
if (this.equals(other)) {
return 0;
}
int s1 = references.size();
int s2 = other.references.size();
if (s1 == s2) {
return -1; // not 0, they are NOT equal!
}
return s1 - s2;
}