1

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?

Nayuki
  • 17,911
  • 6
  • 53
  • 80
Karibasappa G C
  • 2,686
  • 1
  • 18
  • 27

4 Answers4

7

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.

awksp
  • 11,764
  • 4
  • 37
  • 44
AlexR
  • 114,158
  • 16
  • 130
  • 208
  • i agree with you Alex but i took age just for easy example here...my question is why cant we use comparable here ? we can go for comparator iff we need sorting on different properties based on scenario but here i have only one scenario – Karibasappa G C Jun 17 '14 at 19:35
  • and if you say , it is confusing if compaeTo() returns 0 while equals() returns false then how my compareTo() should be to sort person only on age, can you please show me? – Karibasappa G C Jun 17 '14 at 19:37
  • Comparable is a bad design. It is actually a mistake of java designers together with `serializatble`, `equals()`, `hashCode()`, Swing's `paint()` etc. Inheritance and polymorphysm are cool, but this is not the only OO design pattern exists. Delegation is typically more flexible. – AlexR Jun 17 '14 at 19:43
  • thats okie..but how i should write equality consistant compareTo() method..can you show it for Person class itself...sorting should happen on age and equals should return true if compareTo() return 0 – Karibasappa G C Jun 17 '14 at 19:46
  • @AlexR If one were creating a class to represent a rational number, `Comparable` would be a fine design, because there's definitely a natural ordering to those. So I don't think it's necessarily a bad design--it just shouldn't be used where it doesn't make sense, like comparing properties of a `Person`. – ajb Jun 18 '14 at 01:32
  • @ajb, u r right. I would like to fix my previous comment. `Comparable` interface is good if it is implemented consistently. I believe that if `equals()` returns `true`, `compareTo()` should return 0 and vice versa. I think that it is bad practice if `compareTo()` does not use the same fields and similar logic as `equals()` Moreover, i'd say that `equals()` should call `compareTo()` and return result of comparison of its return value with `0`. – AlexR Jun 18 '14 at 06:22
  • @KaribasappaGC "*equality consistant compareTo()*" You'd have to use every field in a lexicographical comparison. This makes little sense for `Person`, so don't do it, unless you really have to. It may make sense for `class Name {String firstname, surname;}`, but `Comparator` is better again (as you may change your mind concening the importance of first name vs. surname). – maaartinus Jun 18 '14 at 14:21
6

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.

ajb
  • 31,309
  • 3
  • 58
  • 84
  • if this is the case compareTo returns 0 if and only if equals returns true..then how can we sort based on just one property ? – Karibasappa G C Jun 17 '14 at 19:24
  • @KaribasappaGC Sorry, I'm not following... If you want to set up different `Comparators` to sort on different properties, then this doesn't apply. This only applies if you're using `Comparable`, which defines _one_ "natural ordering". But I think AlexR might be right, that "age" is not a good "natural ordering" for a `Person` class. – ajb Jun 17 '14 at 19:29
  • @KaribasappaGC You can't. You'd have to use all properties and this often makes no sense. So just don't implement `Comparable`. – maaartinus Jun 18 '14 at 14:23
5

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.

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
  • ya but our code quality tool has put that i need to override equals method and it has come in a major suggestion not minor one..so wondering – Karibasappa G C Jun 17 '14 at 19:15
  • 1
    It is also a good thing to override `hashcode()` in such a case. Josh Bloch provides a very good explanation on this in his Effective Java book. – Laf Jun 17 '14 at 19:16
  • @KaribasappaGC As I said, you don't _need_ to override equals. It's just very highly suggested. Just like why you should override hashCode when overriding equals. – Anubian Noob Jun 17 '14 at 19:17
2

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

Community
  • 1
  • 1
kunal
  • 93
  • 1
  • 2
  • 7