Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
291 views
in Technique[技术] by (71.8m points)

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

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 eg. TreeSet behaves like comapareTo()==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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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."


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...