当前位置: 首页 > 知识库问答 >
问题:

比较器和违反设定合同

巫马安怡
2023-03-14

比较器的JavaDoc指出

强烈建议(尽管不是必需的)自然顺序与相等一致。

他们还举了一个“奇怪”行为的例子,当<代码>(a.equals(b)

现在,有人能给我举一个例子说明在case(!a.equals(b)中的“奇怪”行为吗

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


共有3个答案

狄兴业
2023-03-14

假设以下API:

final class Foo {
    int bar;
    Foo(int bar) { this.bar = bar; }
    public int hashCode() {
        return bar;
    }
    public boolean equals(Object o) {
        return o instanceof Foo && ((Foo)o).bar == bar;
    }
}

static Set<Foo> getSetOpaquely() {???}

我不知道这台电视机是从哪里来的,只是我需要用它。

假设我假设这个集合类似于HashSet,并且定义为等于

他们还举了一个“奇怪”行为的例子,当<代码>(a.equals(b)

假设我知道

Set<Foo> mySet = getSetOpaquely();
mySet.add(new Foo(1));
System.out.println(mySet.add(new Foo(1));

假设我的惊讶,当这打印true,因为它是一个带比较器的TreeSet

(lhs, rhs) -> lhs == rhs ? 0 : 1

现在,有人能给我举一个例子说明在case(!a.equals(b)中的“奇怪”行为吗

假设我知道

Set<Foo> mySet = getSetOpaquely();
mySet.add(new Foo(102));
System.out.println(mySet.add(new Foo(12));

假设这是一个带有比较器的树集,当它打印出false时我感到惊讶

(lhs, rhs) -> Integer.compare(lhs.bar % 10, rhs.bar % 10)

现在,定义与equals不一致的顺序并不存在固有的问题。关键是树集的行为可能与集合的文档中指定的行为不同。

这是有明确记录的:

[...]Set接口是根据equals操作定义的,但是TreeSet实例使用其compareTo(或比较)方法执行所有元素比较,因此两个元素被视为从集合的角度来看,通过这种方法相等。集合的行为是明确定义的,即使它的顺序与等号不一致;它只是不遵守Set接口的一般契约。

只要比较器不是黑客,并且你知道它是一个具有特定顺序的树集,你就不会感到惊讶。(如果它是hacky像(lhs, rhs)-

吴胜涝
2023-03-14

集合的JDK实现依赖于这样的行为关系。另一个很好的例子是HashSet,它依赖于equals()hashCode()同意。

他们所说的“奇怪”是指“未定义的”——如果这些类与不遵守规则的类一起工作,它们将如何工作是没有定义的。它们可能完美地工作,也可能不完美。但是,如果元素类不遵循javadoc中描述的行为,就不能指望它们正常工作。

端木野
2023-03-14

这里有一个简单的演示。我们有一个名为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使用compareTo,而HashSet使用equalshashCode。这可能会以许多不同的方式(也是最重要的意外方式)破坏事物,因为您不必知道幕后正在使用什么类型的Set

这演示了(a.equals(b)

 类似资料:
  • 下面的代码是Dave Koelle的AlphanumComparator的编辑版本。编辑包含将空字符串排序到列表末尾或 底部的代码。问题是 为了解决我的问题,我调查了它并找到了诸如比较器没有 等原因; 在正确的位置。我还在Java错误数据库中发现了一条评论,上面写着 java.util.Arrays.sort和java.util.Collections.sort(间接地)使用的排序算法被替换了,新

  • 我已经在类上实现了Comaprable,它给了我比较方法违反了它的总合同!,由于有一些值返回为 null,代码如下 公共静态比较器名称比较器 = 新比较器() {

  • 我有一个类字段,和。我需要使用对它们进行排序,但我得到了一个异常: java.lang.IllegalArgumentException:比较方法违反了它的一般约定! 我的< code>compareTo方法: 请帮我找出compareTo方法中的错误。谢了。

  • 我试图使用比较器基于两个字符串的比较对数组列表进行排序,但我最终使用的比较方法违反了它的一般契约错误。如果字符串中出现空值比较,我该如何处理? 密码 错误

  • 我目前正在Java中对集合进行排序。我收到了错误消息“比较方法违反了它的一般契约”。我也理解这个错误消息,但我(主要)使用Long类型的构建比较方法。所以我不知道,在这种情况下,排序方法仍然违反了契约。这是我的代码: 这里是错误:

  • 我对collection.sort()方法有一些问题。 Java . lang . illegalargumentexception:比较法违反了它的通用契约! 位于Java . util . Tim sort . merge lo(Tim sort . Java:747) at java.util.TimSort.mergeAt(TimSort.java:483) 位于java.util.Tim