compareTo()与equals()不一致的后果

时间:2013-03-22 14:27:41

标签: java

compareTo()与某个类的equals()不一致时,有人可以说明后果是什么。我已经读过,如果Obj1.compareTo(Obj2) = 0那么Obj1.equals(Obj2) = true并非强制要求。但如果发生这种情况会有什么后果。感谢。

3 个答案:

答案 0 :(得分:11)

Comparable的文档详细说明了这一点:

  

C的自然顺序与equals一致,当且仅当e1.compareTo(e2) == 0e1.equals(e2)具有相同的布尔值时才e1e2的{​​}和C。请注意,null不是任何类的实例,即使e.compareTo(null)返回NullPointerExceptione.equals(null)也应该抛出false

     

强烈建议(尽管不要求)自然排序与equals一致。这是因为没有显式比较器的有序集(和有序映射)在与自然排序与equals不一致的元素(或键)一起使用时表现得“奇怪”。特别是,这样的有序集(或有序映射)违反了集合(或映射)的一般契约,它是根据equals方法定义的。

     

例如,如果将ab两个键(!a.equals(b) && a.compareTo(b) == 0)添加到不使用显式比较器的有序集合中,则第二个添加操作将返回{{1} (并且有序集的大小不会增加)因为falsea从排序集的角度来看是等效的。

     

实际上,所有实现b的Java核心类都具有与Comparable一致的自然排序。一个例外是equals,其自然排序等同java.math.BigDecimal个对象具有相同的值和不同的精度(例如BigDecimal4.0)。

答案 1 :(得分:3)

虽然文档说一致性不是强制性的,但最好始终确保这种一致性,因为您永远不知道您的对象是否有一天出现在TreeMap / TreeSet之类的。如果compareTo()对于不相等的2个对象返回0,则所有基于树的集合都将被破坏。

例如,想象一个类Query,实现一个SQL查询,有两个字段:

  • 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;
}

这样做似乎没问题,因为在两个单独的字段上完成了相等和排序,到目前为止一直很好。

但是,每当放入TreeSetTreeMap时,都是灾难性的:这些类的实现认为如果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;
}

答案 2 :(得分:0)

有些集合会假设如果两个对象跟随obj1.compareTo(obj2) = 0,那么obj1.equals(obj2)也是如此。例如:已排序TreeSet。 如果不符合此逻辑,将导致图标集合。看到: Comparator and equals()