"比较方法违反了其总合同!"

n00*_*ter 179 java comparator

有人可以用简单的语言解释我,为什么这个代码会抛出异常,"比较方法违反了它的一般合同!",我该如何修复它?

private int compareParents(Foo s1, Foo s2) {
    if (s1.getParent() == s2) return -1;
    if (s2.getParent() == s1) return 1;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

NPE*_*NPE 249

你的比较器不是传递性的.

让我们A成为父母B,并B成为她们的父母C.由于A > BB > C,那么它必须是该案件A > C.但是,如果在A和上调用比较器C,它将返回零,这意味着A == C.这违反了合同,因此抛出异常.

图书馆检测到这个并让你知道,而不是表现得不正常,这是相当不错的.

满足传递性要求的一种方法compareParents()是遍历getParent()链而不是仅仅查看直接的祖先.

  • 图书馆检测到这一点的事实非常棒.太阳的人应该抛弃一个巨人_你是受欢迎的. (38认同)
  • 在Java 7的`java.util.Arrays.sort`中引入http://stackoverflow.com/questions/7849539/comparison-method-violates-its-general-contract-java-7-only (3认同)
  • @Qix-MONICAWASMISTREATED 为了澄清历史,TimSort 到 Java 的移植是由当时在 Google 工作的 Josh Bloch(前 Sun 公司)编写的,并于 2009 年 7 月由 Sun 人员集成到 OpenJDK 7 开发线中。这是在甲骨文进入市场之前。不过,Oracle 负责提供 JDK 7 版本。[JDK 7 变更集](http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/bfd7abda8f79) – [错误报告](https://bugs.openjdk.org/browse/JDK-6804124) (2认同)
  • @isapir 请参阅之前的评论。 (2认同)

you*_*786 36

只是因为这是我在谷歌搜索这个错误时得到的,我的问题是我有

if (value < other.value)
  return -1;
else if (value >= other.value)
  return 1;
else
  return 0;
Run Code Online (Sandbox Code Playgroud)

value >= other.value应(显然)实际上是value > other.value让你可以实际上等于对象返回0.

  • 我必须补充一点,如果你的'value`中的任何一个是NaN(如果`value`是`double`或`float`),那么它也会失败. (7认同)

小智 22

违反合同通常意味着比较器在比较对象时没有提供正确或一致的值.例如,您可能希望执行字符串比较并强制将空字符串排序到最后:

if ( one.length() == 0 ) {
    return 1;                   // empty string sorts last
}
if ( two.length() == 0 ) {
    return -1;                  // empty string sorts last                  
}
return one.compareToIgnoreCase( two );
Run Code Online (Sandbox Code Playgroud)

但是这忽略了一个和两个都是空的情况 - 在这种情况下,返回错误的值(1而不是0表示匹配),并且比较器报告为违规.应该写成:

if ( one.length() == 0 ) {
    if ( two.length() == 0 ) {
        return 0;               // BOth empty - so indicate
    }
    return 1;                   // empty string sorts last
}
if ( two.length() == 0 ) {
    return -1;                  // empty string sorts last                  
}
return one.compareToIgnoreCase( two );
Run Code Online (Sandbox Code Playgroud)


Ali*_*zan 11

即使你的compareTo在理论上保持传递性,有时候微妙的错误会让事情变得混乱......例如浮点运算错误.它发生在我身上.这是我的代码:

public int compareTo(tfidfContainer compareTfidf) {
    //descending order
    if (this.tfidf > compareTfidf.tfidf)
        return -1;
    else if (this.tfidf < compareTfidf.tfidf)
        return 1;
    else
        return 0;

}   
Run Code Online (Sandbox Code Playgroud)

传递属性显然是成立的,但由于某种原因,我得到了IllegalArgumentException.事实证明,由于浮点算法中的微小错误,导致传递属性在不应该的地方突破的舍入错误!所以我重新编写了代码来考虑真正微小的差异0,并且它有效:

public int compareTo(tfidfContainer compareTfidf) {
    //descending order
    if ((this.tfidf - compareTfidf.tfidf) < .000000001)
        return 0;
    if (this.tfidf > compareTfidf.tfidf)
        return -1;
    else if (this.tfidf < compareTfidf.tfidf)
        return 1;
    return 0;
}   
Run Code Online (Sandbox Code Playgroud)

  • 这很有帮助!我的代码在逻辑上还可以,但是由于精度问题而出现了错误。 (2认同)

Unc*_*roh 6

在我们的情况下得到了这个错误,因为我们不小心翻了s1和s2的比较顺序.所以要小心.这显然比以下更复杂,但这是一个例子:

s1 == s2   
    return 0;
s2 > s1 
    return 1;
s1 < s2 
    return -1;
Run Code Online (Sandbox Code Playgroud)


小智 6

编辑 VM 配置对我有用。

-Djava.util.Arrays.useLegacyMergeSort=true
Run Code Online (Sandbox Code Playgroud)

  • 另请解释这如何帮助解决所描述的问题。目前它实际上只是一个仅代码的答案。 (3认同)