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。
- java.lang.UnsatisfiedLinkError – JNI
- Java – 以编程方式关闭TestNG的默认记者
- Java 8 lambda表达式:按顺序对满足给定谓词的所有项进行分组。 分组应将谓词作为关键,将项目作为值
- 使用LingPipe对朴素贝叶斯进行数据分类
- 如何在滚动窗格上放置多个标签,为什么这个标签放在中心?
- 单击时取消选择javafx ListView上的项目
- PHP中的PHP的`preg_match_all`function
- javax.annotations.Nonnull上的TypeNotPresentException
- 为什么在通过列表模型更新内容后,我有时会得到空白的JLists?