【问题标题】:why does my compare method throw exception -- Comparison method violates its general contract!为什么我的比较方法会抛出异常——比较方法违反了它的一般合同!
【发布时间】:2011-10-01 08:53:57
【问题描述】:

为什么会有这段代码

public class SponsoredComparator implements Comparator<SRE> {

    public boolean equals(SRE arg0, SRE arg1){
        return arg0.getSponsored()==arg1.getSponsored();
    }

    public int compare(SRE object1, SRE object2) {
        Log.d("SponsoredComparator","object1.getName() == "+ object1.getName());
        Log.d("SponsoredComparator","object1.getSponsored() == "+ object1.getSponsored());
        Log.d("SponsoredComparator","object2.getName() == "+ object2.getName());
        Log.d("SponsoredComparator","object2.getSponsored() == "+ object2.getSponsored());
        Log.d("SponsoredComparator","compare return == "+ (object1.getSponsored() && object2.getSponsored() ? 0 : object1.getSponsored() ? -1 : 1));
        return object1.getSponsored() && object2.getSponsored() ? 0 : object1.getSponsored() ? -1 : 1;
    }
}

抛出这个异常:ERROR/AndroidRuntime(244): java.lang.IllegalArgumentException: Comparison method violates its general contract!
ERROR/AndroidRuntime(4446): at java.util.TimSort.mergeLo(TimSort.java:743)

方法 sre.getSponsored() 返回一个布尔值。

谢谢。

【问题讨论】:

标签: java android comparator contract


【解决方案1】:

我怀疑问题发生在 没有 值被赞助时。无论你怎么称呼它,它都会返回 1,即

x1.compare(x2) == 1

x2.compare(x1) == 1

这是无效的。

我建议你改变这个:

object1.getSponsored() && object2.getSponsored()

object1.getSponsored() == object2.getSponsored()

在这两个地方。我可能实际上在某处提取具有此签名的方法:

public static int compare(boolean x, boolean y)

然后这样称呼它:

public int compare(SRE object1, SRE object2) {
    return BooleanHelper.compare(object1.getSponsored(), object2.getSponsored());
}

这将使代码更清晰,IMO。

【讨论】:

  • 这与您遇到的问题没有直接关系,但是您不必担心空值吗?
【解决方案2】:

我假设您使用的是 JDK 7。请检查以下 URL:

来自http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#source

区域:API:实用程序

概要:ArraysCollections 的更新排序行为可能会抛出 IllegalArgumentException

描述:java.util.Arrays.sort和使用的排序算法 (间接)java.util.Collections.sort 已被替换。新的 如果检测到 sort 实现可能会抛出 IllegalArgumentException 违反Comparable 合同的Comparable。以前的 执行默默地忽略了这种情况。如果上一个 行为是需要的,您可以使用新系统 属性,java.util.Arrays.useLegacyMergeSort,恢复以前的 合并排序行为。

不兼容的性质:行为

RFE:6804124

更多详细信息,请查看错误数据库reference here

【讨论】:

  • 你几乎是对的。 OP 使用 Android(而不是 JDK 7),但您的参考仍然是正确的,因为 JDK 7 和 Android 都使用 TimSort 算法,如果与违反 Comparable 合同的 IllegalArgumentException 一起使用,则会抛出 IllegalArgumentException
【解决方案3】:

equals() 和 compareTo() 的约定是当 equals() 返回 true 时 compareTo() 应该返回 0,当 equals() 为 false 时 compareTo 应该返回 -1 或 +1。

顺便说一句:我假设您的 compare() 方法不会经常调用,因为调试消息会占用大量 CPU 和内存。

【讨论】:

  • 那些调试信息只是为了帮助修复这个错误。
  • 如文档所述“强烈建议,但不严格要求 (x.compareTo(y)==0) == (x.equals(y))”。并且这个问题的例外并不是源于使用 equals 方法打破合同。我在这个旧答案上写了这个评论,因为它导致我得出错误的结论,即在这个异常中违反合同可以与 equals 方法联系起来
  • @Peter Lawrey 这是我发现的第一个信息,有人明确了合同的真正含义。谢谢你但是鉴于equals必须返回一个对应于compareTo的值,我认为所有实现compareTo的用户的最佳解决方案可能是覆盖equals并在此方法中简单地调用compareTo,如果结果返回true为 0,如果不为 0,则为 false。唯一需要注意的是,在 compareTo 中不能调用 equals。
  • @zreptil 使用 compareTo 有一些问题; a) 有时两个值可以相等,但 compareTo 不为 0,例如-0.0 和 0.0 以及 BigDecimal 值。另一个问题是两个值可能没有任何合理的更高或更低的概念,你不能检查是否相等。
  • @Peter Lawrey 我认为 compareTo 可以毫无问题地使用。它为我工作了多年,直到 java-Team 决定违反 compareTo 和 equals 之间的合同必须引发异常,这并不能真正澄清出了什么问题。我很少使用 compareTo,但它是比较一个类的两个实例以确定哪个必须在另一个之前排序的一种方便的方法。在了解合同的详细信息后,该异常的错误修复非常简单,并且工作起来就像一个魅力。
【解决方案4】:

我特别同意 jon 的所有回答,但还有一点我想说的是,我们应该始终在 compare 方法中检查 null 安全性,以便我们的方法永远不会被破坏,并且在编程中始终检查 null 是一个好习惯。更多信息请看here

【讨论】:

    【解决方案5】:

    也许您只有通过 Collections.sort 比较的 NaN 值...这对我来说是个问题,即使正确实现了 compare(obj1, obj2) 方法,我也遇到了这个异常!检查一下!

    【讨论】:

      【解决方案6】:

      我今天在 Web 应用程序中遇到了同样的问题。处理同一个数组的四个调用试图同时对它进行排序,实际上互相搞砸了。

      【讨论】:

        【解决方案7】:

        我的解决方案:当我想对数字进行排序并且数组元素为空时,我输入 0 然后错误消失。需要注意二维数组中每一行的大小是相同的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多