java 为什么如果 compareTo() 返回 0 则暗示对象相等?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7229977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why it is implied that objects are equal if compareTo() returns 0?
提问by Alpedar
Let's have a class Person
. Person has a name and height.
来上课吧Person
。人有名字和身高。
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.
Equals 和 hashCode() 只考虑名称。Person 是可比较的(或者我们为它实现了比较器,不管是哪一个)。人是按身高来比较的。
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.
预期两个不同的人可以具有相同高度的情况似乎是合理的,但是例如。TreeSet 的行为类似于 comapareTo()==0 意味着等于,而不仅仅是大小相同。
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?
你知道为什么会这样吗?
采纳答案by Lukas Eder
Extract from the java.util.SortedSet
javadoc:
从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.
请注意,如果排序集要正确实现 Set 接口,则排序集维护的排序(无论是否提供显式比较器)必须与 equals 一致。(请参阅 Comparable 接口或 Comparator 接口以了解与等于一致的精确定义。)这是因为 Set 接口是根据等于操作定义的,但有序集合使用其 compareTo(或 compare)方法执行所有元素比较,所以被这个方法认为相等的两个元素,从有序集合的角度来看,是相等的。有序集合的行为是明确定义的,即使它的排序与 equals 不一致;它只是不遵守 Set 接口的一般约定。
Hence, in other words, SortedSet
breaks (or "extends") the general contracts for Object.equals()
and Comparable.compareTo
. See the contract for compareTo
:
因此,换句话说,SortedSet
打破(或“扩展”)了Object.equals()
和的一般契约Comparable.compareTo
。见合同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."
强烈推荐,但不严格要求 (x.compareTo(y)==0) == (x.equals(y))。一般而言,任何实现 Comparable 接口并违反此条件的类都应清楚表明这一事实。推荐的语言是“注意:这个类有一个与equals不一致的自然顺序。”
回答by nfechner
It is recommended that compareTo
only returns 0
, if a call to equals
on the same objects would return true
:
建议compareTo
只返回0
,如果equals
对相同对象的调用将返回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. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.
当且仅当 e1.compareTo(e2) == 0 对于类 C 的每个 e1 和 e2 具有与 e1.equals(e2) 相同的布尔值时,才说类 C 的自然顺序与 equals 一致。请注意, null 不是任何类的实例,即使 e.equals(null) 返回 false,e.compareTo(null) 也应该抛出 NullPointerException。
(From the JDK 1.6 Javadocs)
(来自JDK 1.6 Javadocs)
回答by Jon Skeet
TreeSet
doesn't operate using hash codes and equality - it only operates on the basis of the comparator you give it. Note that the Javadocstates:
TreeSet
不使用哈希码和等式进行操作 - 它仅根据您提供的比较器进行操作。请注意,Javadoc指出:
Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator 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 TreeSet instance 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 set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
请注意,如果要正确实现 Set 接口,则集合维护的排序(无论是否提供显式比较器)必须与 equals 一致。(请参阅 Comparable 或 Comparator 以获取与 equals 一致的精确定义。)这是因为 Set 接口是根据 equals 操作定义的,但是 TreeSet 实例使用它的 compareTo(或 compare)方法执行所有元素比较,所以两个被此方法视为相等的元素,从集合的角度来看,是相等的。集合的行为是明确定义的,即使它的顺序与 equals 不一致;它只是不遵守 Set 接口的一般约定。
In your case, your comparison *isn't consistent with equals
, so your set doesn't obey the general contract of Set
.
在您的情况下,您的比较 * 与 不一致equals
,因此您的集合不遵守 的一般契约Set
。
Why not just add more aspects to the comparison so that only equal elements compare with a result of 0?
为什么不在比较中添加更多方面,以便只有相等的元素与结果 0 进行比较?
回答by ratchet freak
you can fix it by using name for another comparison when the heights are equal
当高度相等时,您可以通过使用名称进行另一次比较来修复它
@Override
public int compareTo(Person o) {
if(height == o.height)return name.compareTo(o.name);
return Integer.compare(height, o.height);
}
since names are unique this will only return 0 if this.equals(o)
由于名称是唯一的,因此只有在以下情况下才会返回 0 this.equals(o)
回答by OscarRyz
It is strongly recommended, but not strictlyrequired that(x.compareTo(y)==0) == (x.equals(y))
[1]
强烈推荐,但不严格要求(x.compareTo(y)==0) == (x.equals(y))
[ 1]
So it's fine that you compare with a different criteria than the used on equals
granted that you document it.
因此,您可以使用与equals
记录它时使用的标准不同的标准进行比较。
However it would be better if your compare by the same criteria and if needed provide a custom comparator which works on the new criteria ( height in the case of Person )
但是,如果您按照相同的标准进行比较并且需要时提供一个适用于新标准的自定义比较器(在 Person 的情况下为 height )会更好
回答by S.L. Barth - Reinstate Monica
When you give Person a Comparator that compares instances on the height attribute of the Person, it really means that two Person instances are the same if they have the same height. You will have to make a Comparator that is specific for class Person.
当你给 Person 一个 Comparator 来比较 Person 的 height 属性上的实例时,如果两个 Person 实例具有相同的高度,那就真的意味着它们是相同的。您必须制作一个特定于类 Person 的比较器。