Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why it is implied that objects are equal if compareTo() returns 0?

Tags:

Let's have a class Person. Person has a name and height.

Equals and hashCode() takes into account only name. Person is comparable (or we implement comparator for it, does not matter which one). Persons are compared by height.

It seems reasonable to expect a situation where two different persons can have same height, but e.g. TreeSet behaves like compareTo()==0 means equals, not merely same size.

To avoid this, comparison can secondarily look at something else if size is the same, but then it cannot be used to detect same sized different objects.

Example:

import java.util.Comparator; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.TreeSet;  public class Person implements Comparable<Person> {  private final String name; private int height;  public Person(String name,         int height) {     this.name = name;     this.height = height; }  public int getHeight() {     return height; }  public void setHeight(int height) {     this.height = height; }  public String getName() {     return name; }  @Override public int compareTo(Person o) {     return Integer.compare(height, o.height); }  public boolean equals(Object obj) {     if (obj == null) {         return false;     }     if (getClass() != obj.getClass()) {         return false;     }     final Person other = (Person) obj;     if (!Objects.equals(this.name, other.name)) {         return false;     }     return true; }  public int hashCode() {     int hash = 5;     hash = 13 * hash + Objects.hashCode(this.name);     return hash; }  public String toString() {     return "Person{" + name + ", height = " + height + '}'; }  public static class PComparator1 implements Comparator<Person> {      @Override     public int compare(Person o1,             Person o2) {         return o1.compareTo(o2);     } }  public static class PComparator2 implements Comparator<Person> {      @Override     public int compare(Person o1,             Person o2) {         int r = Integer.compare(o1.height, o2.height);         return r == 0 ? o1.name.compareTo(o2.name) : r;     } }  public static void test(Set<Person> ps) {     ps.add(new Person("Ann", 150));     ps.add(new Person("Jane", 150));     ps.add(new Person("John", 180));     System.out.println(ps.getClass().getName());     for (Person p : ps) {         System.out.println(" " + p);     } }  public static void main(String[] args) {     test(new HashSet<Person>());     test(new TreeSet<Person>());     test(new TreeSet<>(new PComparator1()));     test(new TreeSet<>(new PComparator2())); } } 

result:

java.util.HashSet  Person{Ann, height = 150}  Person{John, height = 180}  Person{Jane, height = 150}  java.util.TreeSet  Person{Ann, height = 150}  Person{John, height = 180}  java.util.TreeSet  Person{Ann, height = 150}  Person{John, height = 180}  java.util.TreeSet  Person{Ann, height = 150}  Person{Jane, height = 150}  Person{John, height = 180} 

Do you have idea why it is so?

like image 613
Alpedar Avatar asked Aug 29 '11 12:08

Alpedar


People also ask

Will two objects always be equal when their compareTo method returns zero?

It is recommended that compareTo only returns 0 , if a call to equals on the same objects would return true : The natural ordering for a class C is said to be consistent with equals if and only if e1. compareTo(e2) == 0 has the same boolean value as e1. equals(e2) for every e1 and e2 of class C.

Why compareTo () should be consistent to equals () method in Java?

2) CompareTo must be in consistent with equals method e.g. if two objects are equal via equals() , there compareTo() must return zero otherwise if those objects are stored in SortedSet or SortedMap they will not behave properly.

What does compareTo 0 mean?

Java String compareTo() MethodThe method returns 0 if the string is equal to the other string. A value less than 0 is returned if the string is less than the other string (less characters) and a value greater than 0 if the string is greater than the other string (more characters).

What happens when comparator returns 0?

This is mostly used for sorting, so the results returned by compare(...) are a comparison of ranks and 0 means equality in rank.


1 Answers

Extract from the java.util.SortedSet javadoc:

Note that the ordering maintained by a sorted set (whether or not an explicit comparator is provided) must be consistent with equals if the sorted set is to correctly implement the Set interface. (See the Comparable interface or Comparator interface for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a sorted set performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the sorted set, equal. The behavior of a sorted set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.

Hence, in other words, SortedSet breaks (or "extends") the general contracts for Object.equals() and Comparable.compareTo. See the contract for compareTo:

It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."

like image 111
Lukas Eder Avatar answered Sep 20 '22 19:09

Lukas Eder