【问题标题】:Another "Comparison method violates its general contract!"另一个“比较方法违反了它的总合同!”
【发布时间】:2013-08-07 23:30:09
【问题描述】:

作为实习的一部分,我被要求调查一个错误。 一段代码在抛出

java.lang.IllegalArgumentException:比较方法违反了它的一般约定!

自定义Comparator 正在通过查看所述自定义类的long 成员变量来比较两个自定义类:

return v1 > v2 ? -1 : v1 < v2 ? 1 : 0;

此自定义类的equals 方法查看此自定义类的String 成员变量。 我们在重现这种行为时遇到了麻烦。 我的下意识反应是用return v2.compareTo(v1); 替换自定义Comparator 中的return 语句, 但我的团队怀疑这是否能解决问题。 谁能提供任何见解?

Arrays.sort(anArray, new Comparator<ACustomClass>() {
  @Override
  public int compare(ACustomClass o1, ACustomClass o2) {
    long v1 = o1.getALong();
    long v2 = o2.getALong();
    return v1 > v2 ? -1 : v1 < v2 ? 1 : 0;
  }});

【问题讨论】:

  • 有机会发布一些实际代码吗?
  • 你说你无法重现这个问题,但你也说你知道哪段代码抛出了异常。是哪个?
  • Comparator documentation 有一些关于“等于”一致性的注释。我想说的是,如果 Comparator 在 equals() 返回 false 时可能返回 0,或者如果 Comparator 在 equals() 返回 true 时返回非零,那么您可能会遇到麻烦。
  • 你为什么不在这里使用Long.compare()
  • 跟进我之前的评论:我快速查看了运行时源,消息来自 TimSort 或 ComparableTimSort。这些类似乎没有直接调用 equals(),但它们可能会调用其他使用 equals() 的东西。但我需要做很多工作才能进一步深入研究。

标签: java java-7 comparator illegalargumentexception


【解决方案1】:

我看不出所呈现的比较器有任何明显错误。 (而且我对提议的修复持怀疑态度:它们对我来说是巫毒编程的“气味”。)

但是如果ACustomClass 类的aLong 属性是可变的...并且在您排序时它发生了变化...那么这可能会导致排序代码认为比较器违反了合同。

所以...检查一下这是否可能是一个并发问题,其中一个线程正在改变另一个线程正在尝试排序的数组中的对象。


我们在调试器上花了很多时间...很多不同的测试用例。无法重现该行为。

我会将其视为指向并发问题的证据...

【讨论】:

  • 确切的“IllegalArgumentException:比较方法违反了它的一般合同!”仅在 Java 7 TimSort 中出现,当根据 自然顺序 进行排序时。使用Comparator 时,绝不应该出现该特定消息。
  • 我认为这不是真的,@MarkoTopolnik。
  • @StephenC - 感谢您的洞察力。我们进行了一些测试,看看事实是否如此,看来您是正确的。
  • @bond - “排序函数不应该假设对象是不可变的。” - 它必须!在排序时,对“排序”关系正在发生变化的对象集合进行排序在数学上是不可能的。这不是 JDK 中的设计缺陷。试图做到这一点的应用程序存在设计缺陷......不可能的事情。
  • 好吧,sort 方法的 Java API 合同不适用于“足够好”的排序。它用于精确排序,而且一直如此。如果你想要/需要“足够好”的排序,你需要实现自己的排序方法。
【解决方案2】:

我认为你在这里找错树了。那段代码不会抛出该异常。我想查看堆栈跟踪,并且还想查看ACustomClass.equals() 方法。除非它测试 getAlong() 结果的相等性,没有别的,它不同意这个 Comparator,,因此如果在与 equals 一致的上下文中使用其中一个是错误的必需的,例如排序的集合,这更可能是引发异常的地方。

您可以通过实验解决怀疑。除非他们能提出一个实际的正式原因说明它为什么不能工作,否则你当然有权尝试它。

【讨论】:

  • equals 不必与比较器保持一致。 Arrays.sort 不使用 equals
  • @arshajii 在 Javadoc 中并没有这么说,但它确实说“在使用能够施加与 equals 不一致的排序的比较器来排序排序集(或排序映射)时,应谨慎行事)”。如果我们查看源代码,它也不会抛出异常。
猜你喜欢
  • 2016-11-18
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多