如果compareTo()返回0,则隐含对象相等?

问题描述 投票:19回答:6

让我们上一堂课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<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()));
}
}

结果:

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 collections equals comparable compareto
6个回答
18
投票

[java.util.SortedSet javadoc的摘录:

请注意,排序顺序由排序集(无论是否如果提供显式比较器,则必须与equals保持一致,如果排序后的集合是为了正确实现Set接口。 (请参阅可比较的接口或比较器接口,用于精确定义与equals保持一致。)之所以如此,是因为Set接口是根据equals操作定义,但排序后的集合执行所有元素比较都使用其compareTo(或compare)方法,因此从此方法中,被该方法视为相等的两个元素是排序集的立场,相等。排序集的行为是定义明确,即使其顺序与等式不一致;这只是无法遵守Set接口的常规协定。

因此,SortedSet破坏(或“扩展”)Object.equals()Comparable.compareTo的一般合同。请参见compareTo的合同:

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


10
投票

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

[且仅当e1.compareTo(e2)== 0对C类的每个e1和e2具有与e1.equals(e2)相同的布尔值时,C类的自然顺序才等于equals。请注意,null不是任何类的实例,即使e.equals(null)返回false,e.compareTo(null)也应引发NullPointerException。

(来自JDK 1.6 Javadocs


6
投票

TreeSet不能使用哈希码和等式进行操作-只能在您提供的比较器的基础上进行操作。请注意,Javadoc状态:


1
投票

当高度相等时,您可以通过使用名称进行其他比较来解决它


1
投票

强烈建议,但不严格


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.