java - 如果 compareTo() 返回 0,为什么暗示对象相等?

标签 java collections equals comparable compareto

让我们有一个类Person。人有名字和高度。

Equals 和 hashCode() 只考虑名称。 Person 是可比较的(或者我们为它实现了比较器,不管是哪一个)。人员按高度进行比较。

期望两个不同的人可以具有相同的高度似乎是合理的,但是例如TreeSet 的行为类似于 compareTo()==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.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.

因此,换句话说,SortedSet 打破(或“扩展”)Object.equals()Comparable.compareTo 的一般契约(Contract)>。查看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."

关于java - 如果 compareTo() 返回 0,为什么暗示对象相等?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7229977/

相关文章:

Java,阻塞队列

java - xstream,如何将列表序列化为xml

java - 以下情况的 hashCode() 方法 : two sets are equal when they have at least one element in common?

java - 为什么 assertEquals(new Object[] {"abc"}, new Object[] {"abc"});不失败?

java - 是什么导致 Spring 调度程序在应用程序和 servlet 启动之前执行?

java - 实现单例模式

java - 为什么在我将 JComboBox 添加到其中一个 JPanel 中后,所有 JPanel 都没有呈现

java - List.iterator() 是线程安全的吗?

java - 存储桶是单个内存位置还是类似于内存位置数组?

java - 未调用 paintComponent