【问题标题】:equals method contract with comparable interface具有可比较接口的 equals 方法契约
【发布时间】:2014-06-17 19:12:33
【问题描述】:

我有一个像 Person 这样的自定义类:

public class Person {
    int age;
    String name;
}

现在我想根据agePerson 类对象进行排序。

所以我将使用Comparable接口并实现compareTo()方法。

compareTo 将具有仅基于age 比较人员对象的逻辑。

如果我这样做:

Collections.sort(list);  // where list is a list of person

我将根据age得到排序的人员列表。

但我在某处读到,我们在执行 Comparable 实现时也需要覆盖 equals() 方法。

但我目前还没有看到它的用途。

谁能解释一下,如果我只想基于age 来覆盖sort,哪里还需要覆盖equals() 方法?

【问题讨论】:

    标签: java collections equals comparable


    【解决方案1】:

    没有与Comparable.compareTo()equals() 相关的法律。但是我想说,如果compaeTo() 返回0equals() 返回false,这会令人困惑。

    我认为在你的情况下你应该使用自定义比较器而不是让你的类Comparable。原因是今天你的比较标准是年龄,明天它将是这个人的名字,然后是姓氏,然后是体重......等等。这就是引入Comparator的原因以及应该使用的用例使用。

    【讨论】:

    • 我同意你的观点,亚历克斯,但我在这里只是为了简单的例子......我的问题是为什么我们不能在这里使用可比较的?如果我们需要根据场景对不同属性进行排序,我们可以选择比较器,但这里我只有一个场景
    • 如果你说,如果 compaeTo() 返回 0 而 equals() 返回 false 那么我的 compareTo() 应该如何仅按年龄对人进行排序,你能告诉我吗?
    • Comparable 是一个糟糕的设计。这实际上是 java 设计者的错误以及serializatbleequals()hashCode()、Swing 的paint() 等。继承和多态很酷,但这并不是唯一存在的 OO 设计模式。委派通常更灵活。
    • 没关系..但是我应该如何编写等式一致的 compareTo() 方法..你可以为 Person 类本身展示它吗...排序应该发生在年龄上,如果 compareTo() 应该返回 true返回 0
    • @AlexR 如果要创建一个类来表示一个有理数,Comparable 将是一个很好的设计,因为它们肯定有一个自然的顺序。所以我不认为它一定是一个糟糕的设计——它只是不应该用在没有意义的地方,比如比较Person的属性。
    【解决方案2】:

    来自Comparable的javadoc:

    强烈建议(尽管不是必需的)自然 订单[即Comparable.compareTo] 定义的排序与equals一致。之所以如此,是因为排序集 (和排序的地图)没有显式比较器的行为“奇怪”时 它们与自然顺序为的元素(或键)一起使用 与等号不一致。特别是,这样的排序集(或排序 map) 违反了 set(或 map)的一般约定,该约定被定义为 就equals方法而言。

    “与等号一致”意味着当且仅当equals 返回true 时,compareTo 返回 0。因此,除非compareTo 总是为两个不同的对象引用返回值!= 0,否则equals 应该被覆盖。

    【讨论】:

    • 如果是这种情况compareTo返回0当且仅当equals返回true..那么我们如何仅基于一个属性进行排序?
    • @KaribasappaGC 抱歉,我没有关注...这仅适用于您使用 Comparable,它定义了 one “自然排序”。但我认为 AlexR 可能是对的,对于 Person 类来说,“年龄”并不是一个好的“自然顺序”。
    • @KaribasappaGC 你不能。您必须使用所有属性,这通常没有意义。所以不要实现Comparable
    【解决方案3】:

    您不需要覆盖等号,因为Collections.sort() 将使用compareTo(),而不是equals()。但是,通常最好覆盖它。

    【讨论】:

    • 是的,但是我们的代码质量工具已经提出我需要重写 equals 方法,它提出了一个主要建议而不是次要建议......所以想知道
    • 在这种情况下覆盖hashcode()也是一件好事。 Josh Bloch 在他的 Effective Java 书中对此提供了很好的解释。
    • @KaribasappaGC 正如我所说,您不需要 覆盖等号。这只是非常强烈的建议。就像为什么在覆盖 equals 时应该覆盖 hashCode 一样。
    【解决方案4】:

    只是为了详细说明为什么 compareTo() 和 equals() 需要保持一致。 Sorted Set 依赖于 Object 的 compareTo() 方法来知道是否可以将特定元素添加到集合中,并避免将重复元素添加到集合中。因此,如果是这种情况,则会导致歧义(从规范中复制):-

    例如,如果添加两个键 a 和 b 使得 (!a.equals(b) && a.compareTo(b) == 0) 到不使用显式 比较器,第二个加法操作返回假(和大小 排序集不会增加),因为 a 和 b 等价于 排序集的视角。

    更详细地说,排序集使用 compareTo() 来检查两个元素 a & b 是否相等,如果 a & b 的类只覆盖 compareTo() 方法而不是 equals() 方法,那么我们可以有一种情况,compareTo() 方法返回 0,而 equals() 方法返回 false,这很奇怪且不一致。假设我有一个 Name 类,如下所示:-

    public class Name implements Comparable<Name> {
    private final String firstName, lastName;
    
    public Name(String firstName, String lastName) {
        if (firstName == null || lastName == null)
            throw new NullPointerException();
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String firstName() {
        return firstName;
    }
    
    public String lastName() {
        return lastName;
    }
    
    /*public boolean equals(Object o) {
        if (!(o instanceof Name))
            return false;
        Name n = (Name) o;
        return n.firstName.equals(firstName) && n.lastName.equals(lastName);
    }
    
    public int hashCode() {
        return 31 * firstName.hashCode() + lastName.hashCode();
    }*/
    
    public String toString() {
        return firstName + " " + lastName;
    }
    
    public int compareTo(Name n) {
        int lastCmp = n.lastName.compareTo(lastName);
        return (lastCmp != 0 ? lastCmp : n.firstName.compareTo(firstName));
    }
    

    }

            // Tester class main method has the below code
            Name n1 = new Name("John", "Smith");
            Name n2 = new Name("John", "Smith");
    
            SortedSet<Name> mySet = new TreeSet<>();
    
            System.out.println(mySet.add(n1));
            System.out.println(n1.equals(n2));
            System.out.println(n1.compareTo(n2));
            System.out.println(mySet.add(n2));
    

    输出如下图:-

    true
    false
    0
    false
    

    现在这显示了歧义,因为我们没有覆盖在这种情况下返回 false 的 equals(),但 compareTo() 返回 0,因此 SortedSet 认为两个对象 n1 和 n2 相等,因此将 n2 添加到设置 return false 如 sysout 所示,但它们不相等!!!

    更多请参考stackoverflow链接:-Comparator and equals()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多