equals method contract with comparable interface

自作多情 提交于 2019-12-20 05:43:08

问题


I have a custom class like Person:

public class Person {
    int age;
    String name;
}

Now I want to sort Person class objects based on age.

So I will use Comparable interface and implement compareTo() method.

And compareTo will have logic to compare person object based on just age.

So if I do :

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

I will get sorted person list based on age.

But I read somewhere, we need to override equals() method as well when we do Comparable implementation.

But I am not seeing its use as of now.

Can anyone explain, where there will be a need to override equals() method as well if I want to just sort based on age?


回答1:


There is no law that relates Comparable.compareTo() and equals(). However I'd say that it is confusing if compaeTo() returns 0 while equals() returns false.

I think that in your case you should use custom comparator instead of making your class Comparable. The reason is that today your comparison criteria is age, tomorrow it will be the person's first name, then last name, then weight... etc, etc. This is the reason that Comparator was introduced and the use case when it should be used.




回答2:


From the javadoc for Comparable:

It is strongly recommended (though not required) that natural orderings [i.e. the ordering defined by Comparable.compareTo] be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.

"Consistent with equals" means that compareTo returns 0 if and only if equals returns true. So unless compareTo always returns a value != 0 for two different object references, equals should be overridden.




回答3:


You don't need to override equals, since Collections.sort() will use compareTo(), not equals(). However, it's generally a good idea to override it.




回答4:


Just to elaborate more on this why compareTo() and equals() need to be consistent. The Sorted Set depends on the Object's compareTo() method to know whether a particular element can be added or not in the set and to avoid duplicate elements from being added into the set. So if this is the case , then it leads to ambiguity(copied from the specs):-

For example, if one adds two keys a and b such that (!a.equals(b) && a.compareTo(b) == 0) to a sorted set that does not use an explicit comparator, the second add operation returns false (and the size of the sorted set does not increase) because a and b are equivalent from the sorted set's perspective.

To elaborate more, sorted set used compareTo() to check if the two elements a & b are equal or not and if the Class for a & b only overrides compareTo() method and not equals() method , then we could have a scenario where compareTo() method returns 0, but equals() method return false, which is quite weird and inconsistent. Let's say I have a Name class as shown below:-

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));

The output is as shown below:-

true
false
0
false

Now this shows the ambiguity since we haven't overridden the equals() which in this case is returning false, but compareTo() is returning 0 and hence SortedSet considers the two objects n1 and n2 equal and hence adding n2 into the Set return false as shown in the sysout but they are not equal!!!

For more please refer to the stackoverflow link:-Comparator and equals()



来源:https://stackoverflow.com/questions/24271616/equals-method-contract-with-comparable-interface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!