java - 比较器和违反集合的约定

标签 java equals comparator contract

Comparator 的 JavaDoc 指出

It is strongly recommended (though not required) that natural orderings be consistent with equals.

他们还给出了 (a.equals(b) && c.compare(a,b) != 0) 时的“奇怪”行为示例。

现在,有人可以给我一个在 (!a.equals(b) && c.compare(a,b) == 0) 情况下“奇怪”行为的例子吗?第二种情况应该比第一种情况更常见,因为在实现 Comparator 时很容易忘记为比较类型实现 equals。如果不知道 TreeSet 的实现,就很难想出一个例子。

(这是一个更长的故事,为什么这个问题与我相关。而且这不是一项家庭作业)

最佳答案

这是一个简单的演示。我们有一个名为 Strange 的类,它使用不区分大小写的字符串比较来实现 equalshashCode,但实现了 compareTo 区分大小写。

class Strange implements Comparable<Strange> {

    final String s;

    public Strange(String s) {
        this.s = s;
    }

    @Override
    public boolean equals(Object o) {
        // Kind of equals - case insensitive.
        return (o instanceof Strange) && ((Strange) o).s.equalsIgnoreCase(s);
    }

    @Override
    public int hashCode() {
        // Consistent with equals.
        return s.toUpperCase().hashCode();
    }

    @Override
    public int compareTo(Strange o) {
        // Exact ordering including case - inconsistent with equals.
        return s.compareTo(o.s);
    }

    @Override
    public String toString() {
        return s;
    }

}

public void test() {
    Set<Strange> set1 = new HashSet<>();
    Set<Strange> set2 = new TreeSet<>();
    for (String s : new String[]{"Hello", "hello", "Everyone", "everyone"}) {
        Strange strange = new Strange(s);
        set1.add(strange);
        set2.add(strange);
    }
    System.out.println("Set1: " + set1);
    System.out.println("Set2: " + set2);
}

我们得到 - 正如您可能期望的那样:

Set1: [Hello, Everyone]
Set2: [Everyone, Hello, everyone, hello]

看看将字符串放入 TreeSet 中会如何改变结果?这是因为 TreeSet 使用 compareTo,而 HashSet 使用 equalshashCode。这可能会以多种不同(最重要的意外)方式破坏事物,因为您不必知道在幕后使用哪种Set

这表明 (a.equals(b) && a.compareTo(b) != 0) 给出了奇怪的结果。很容易证明相反的问题 (!a.equals(b) && a.compareTo(b) == 0) 也表现出奇怪的结果。

关于java - 比较器和违反集合的约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28713998/

相关文章:

java - equals 方法适合我的 Java 类吗

java - equals() 和 hashCode() 的区别

对象上的Java任意比较器

java - TreeMap Key、Value 对存在,但 .get(Key) 返回值为 null

java - 根据内部日期列表对 JAVA 父列表进行排序

java - 在Java中,类中的枚举类型是静态的吗?

java - 我使用spring-cloud-stream无法发送消息,但可以接收消息

java - 覆盖自引用父子对象的等于

java - 将带有 TextField 的 Java 程序转换为 swing JTextField

java - 如何解析字符串以分隔整数? java