6

有人可以阐明与班级compareTo()不一致时的后果是什么。equals()我已经读过,如果Obj1.compareTo(Obj2) = 0那么它不是强制性的Obj1.equals(Obj2) = true。但如果发生这种情况,后果是什么。谢谢。

4

3 回答 3

12

的文档Comparable对此进行了一些详细的解释:

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

强烈建议(尽管不是必需的)自然排序与equals. 之所以如此,是因为没有显式比较器的排序集(和排序映射)在与自然顺序与equals. 特别是,这样的排序集(或排序图)违反了集合(或图)的一般合同,该合同是根据equals方法定义的。

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

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

于 2013-03-22T14:32:14.467 回答
3

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

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

  • tableList:表列表
  • 参考:使用此类查询的程序列表

假设 2 个对象相等,如果它们的 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;
}

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

但是,无论何时放入 aTreeSet或 a 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;
}
于 2015-01-03T14:32:53.467 回答
1

一些集合会假设如果两个对象跟随,obj1.compareTo(obj2) = 0那么obj1.equals(obj2)也是如此。例如:排序TreeSet。未能满足此逻辑将导致图标一致的集合。请参阅: 比较器和 equals()

于 2013-03-22T14:35:26.813 回答