Java,如果compareTo()返回0,为什么暗示对象是相等的?

我们有一个Person 。 人有名字和身高。

Equals和hashCode()仅考虑名称。 人是可比的(或者我们为它实施比较,无论哪一个)。 人员按身高进行比较。

期望两个不同的人可以具有相同高度的情况似乎是合理的,但是例如。 TreeSet的行为类似于comapareTo()== 0表示等于,而不仅仅是相同的大小。

为了避免这种情况,如果大小相同,比较可以继续查看其他内容,但是它不能用于检测相同大小的不同对象。

例:

 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 { 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 { @Override public int compare(Person o1, Person o2) { return o1.compareTo(o2); } } public static class PComparator2 implements Comparator { @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 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()); test(new TreeSet()); test(new TreeSet(new PComparator1())); test(new TreeSet(new PComparator2())); } } 

结果:

 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} 

你知道为什么会这样吗?

java.util.SortedSet javadoc中提取:

请注意,如果有序集合要正确实现Set接口,则由有序集合维护的排序(无论是否提供显式比较器)必须与equals一致。 (有关与equals一致的精确定义,请参阅Comparable接口或Comparator接口。)这是因为Set接口是根据equals操作定义的,但是有序集使用compareTo(或compare)方法执行所有元素比较因此,从排序集的角度来看,这种方法被认为相等的两个元素是相等的。 即使排序与equals不一致,排序集的行为也是明确定义的; 它只是不遵守Set接口的一般合同。

因此,换句话说, SortedSet打破(或“扩展”) Object.equals()Comparable.compareTo Object.equals() 。 查看compareTo的合同:

强烈建议,但并非严格要求(x.compareTo(y)== 0)==(x.equals(y))。 一般来说,任何实现Comparable接口并且违反此条件的类都应该清楚地表明这一事实。 推荐的语言是“注意:此类具有与equals不一致的自然顺序。”

建议compareTo仅返回0 ,如果对相同对象的equals调用将返回true

当且仅当e1.compareTo(e2)== 0与c1的每个e1和e2的e1.equals(e2)具有相同的布尔值时,C类的自然排序被认为与equals一致。 null不是任何类的实例,并且e.compareTo(null)应抛出NullPointerException,即使e.equals(null)返回false。

(来自JDK 1.6 Javadocs )

TreeSet不使用哈希码和相等操作 – 它只在你给它的比较器的基础上运行。 请注意, Javadoc声明:

请注意,如果要正确实现Set接口,则由set维护的排序(无论是否提供显式比较器)必须与equals一致。 (请参阅Comparable或Comparator以获得与equals一致的精确定义。)这是因为Set接口是根据equals操作定义的,但TreeSet实例使用compareTo(或compare)方法执行所有元素比较,因此从该集合的角度来看,通过这种方法被认为相等的元素是相等的。 集合的行为即使其排序与equals不一致也是明确定义的; 它只是不遵守Set接口的一般合同。

在您的情况下,您的比较*与equals不一致,因此您的集合不遵守Set的一般合约。

为什么不在比较中添加更多方面,以便只有相等的元素与0的结果进行比较?

您可以通过在高度相等时使用名称进行另一次比较来修复它

 @Override public int compareTo(Person o) { if(height == o.height)return name.compareTo(o.name); return Integer.compare(height, o.height); } 

由于名称是唯一的,如果this.equals(o)只返回0

强烈建议,但并非严格要求 (x.compareTo(y)==0) == (x.equals(y)) [ 1 ]

所以你可以用不同的标准来比较你所记录的equals你所使用的equals标准的标准。

然而,如果您按照相同的标准进行比较并且如果需要的话,提供一个适用于新标准的自定义比较器(在Person的情况下的高度)会更好

当你给Person一个比较Person的height属性上的实例的比较器时,它实际上意味着如果两个Person实例具有相同的高度,它们是相同的。 您必须制作一个特定于Person类的Comparator。