当前位置: 首页 > 编程笔记 >

java中排序报:Comparison method violates its general contract异常的解决

国胤
2023-03-14
本文向大家介绍java中排序报:Comparison method violates its general contract异常的解决,包括了java中排序报:Comparison method violates its general contract异常的解决的使用技巧和注意事项,需要的朋友参考一下

前言

上周线上的一段排序的java代码出现了一个Comparison method violates its general contract,在解决这个问题的途中学到了一些知识这里总结分享一下。

异常原因

这个排序导致的异常将会在java7以上的版本出现,所以如果你的JDK从6升级到了7或者8,那一定要小心此异常。

在java7的兼容列表中,就有对此排序不兼容的说明:

Area: API: Utilities
Synopsis: Updated sort behavior for Arrays and Collections may throw an IllegalArgumentException
Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation.
If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behavior.
Nature of Incompatibility: behavioral
RFE: 6804124

我从资料中查阅到java7开始引入了Timsort的排序算法。我之前一直以为大部分标准库的内置排序算法都是快速排序。现在才得知很多语言内部都使用Timsort排序。随后我在wiki百科上找到了这样一句话:

t was implemented by Tim Peters in 2002 for use in the Python programming language.

所以这个排序自然是以他命名的。

随后我又在网上找到了这样一张图排序比较的图:

可以发现,Timsort在表现上比QuickSort还要好。

这篇博客不去详细讨论Timsort的实现(看上去这个算法还挺复杂的),我可能会写另一篇博客单独讨论Timsort,简单来说Timsort结合了归并排序和插入排序。这个算法在实现过程中明确需要:严格的单调递增或者递减来保证算法的稳定性。

  • sgn(compare(x, y)) == -sgn(compare(y, x))
  • ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0
  • compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z

看上去很像离散数学课中学习的集合的对称性,传递性的关系。

所以异常的原因是因为排序算法不够严谨导致的,实际上业务上的代码经常不如纯技术上的严谨。比如对于这样一个算法:

选出航班中的最低价

那如果两个相等低价同时存在,按照寻找最低价的逻辑如果这么写:

if (thisPrice < lowPrice){
 lowPrice = thisPrice;
}

那低价这个位置就是“先到先得”了。

但如果这么实现:

if(thisPrice <= lowPrice){
 lowPrice = thisPrice;
}

那后面的低价就会覆盖前面的,变成了“后来者居上”。编程中经常遇到先到先得和后来者居上这两个问题。

所以对于上面那个需要提供严谨的判断大小比较函数实现。所以如果是这样的:

return x > y ? 1 : -1;

那么就不符合此条件。

不过我们逻辑要比这个复杂,其实是这样一个排序条件。按照:

  • 价格进行排序,如果价格相等则起飞时间靠前的先排。
  • 如果起飞时间也相等,就会按照:
  • 非共享非经停>非经停>非共享>经停的属性进行优先级选择,如果这些属性都全部相等,才只能算是相等了。

所以这个判断函数的问题是:

public compareFlightPrice(flightPrice o1, flightPrice o2){
 // 非经停非共享
 if (o1.getStopNumber() == 0 && !o1.isShare()) {
 return -1;
 } else if (o2.getStopNumber() == 0 && !o2.isShare()) {
 return 1;
 } else {
 if (o1.getStopNumber() == 0) {
  return -1;
 } else if (o2.getStopNumber() == 0) {
  return 1;
 } else {
  if (!o1.isShare()) {
  return -1;
  } else if (!o2.isShare()) {
  return 1;
  } else {
  if (o1.getStopNumber() > 0) {
   return -1;
  } else if (o2.getStopNumber() > 0) {
   return 1;
  } else {
   return 0;
  }
  }
 }
 }
}

这个函数有明显的先到先得的问题,比如对于compareFlightPrice(a, b) ,如果ab都是非共享非经停,那么这个就会把a排到前面,但如果调用compareFlightPrice(b, a) ,b又会排到前面,所以必须判断a是非共享非经停且b不是非共享非经停,才能让a排在前面。

当然除了改比较函数,还有一个解决方式是:给jvm添加启动参数

-Djava.util.Arrays.useLegacyMergeSort=true

还需要注意的是,并不一定你的集合中存在相等的元素,并且比较函数不符合上面的严谨定义,就一定会稳定浮现此异常,实际上我们在生产环境出现此异常的概率很小,毕竟java并不会蠢到先去把整个数组都校验一遍,实际上它是在排序的过程中发现你不符合此条件的。所以有可能某种集合顺序让你刚好绕过了此判断。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。

 类似资料:
  • 问题内容: 在学习Java时,我经常会偶然发现此错误。它是这样的: 只是一个例子,我见过很多不同的例子。在这种情况下,导致错误的代码是: 一旦将语句放入块中,错误总是消失并且代码编译并成功运行。有时对我来说已经足够了,但有时却不行。 首先,我从中学习的示例并不总是使用,但是显然应该可以使用。 更重要的是,有时当我将整个代码放在中时,它根本无法工作。例如在这种情况下,我需要; 在区块中;但如果上述本

  • 问题内容: 我收到了一个未报告的异常;必须在下面的fill方法中被捕获或声明为抛出错误。从我读过的类似文章中,我假设错误是由read方法引发Exception引发的,但我无法修复。 填充定义为: 问题答案: 您的call ,它被声明为throw ,但是您既没有捕获witin异常,也没有声明它可能被抛出。 最简单的解决方法是将的签名更改为: 我也强烈建议 不要 关闭中的读者。通常,获取资源的相同代码

  • 你好,我试图在NetBeans7 IDE,java6,glassFish3.2环境中生成动态报告。我正在使用java创建项目 commons-collections-3.2.1 commons-digester-2.0 dynamicreports-1.3.0 dynamicreports-adhoc-4.0.1 dynamicreports-core-4.0.1 itext-2.1.7 jasp

  • 然而,试图使用jsp与bean交互时,在运行它时会出现一些错误。 下面是我在浏览器中看到的错误 生成的servlet错误:[javac]c:\sun\appservernew\domains\domain1\Generated\jsp\j2ee-apps\converterapp\war-ic_war\org\apache\jsp\index_jsp.java:24:未报告的异常javax.ejb

  • 本文向大家介绍java中Collections.sort排序详解,包括了java中Collections.sort排序详解的使用技巧和注意事项,需要的朋友参考一下 Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能;如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f,g这样,当然数字也是这样的。 com

  • 本文向大家介绍详解Java中Collections.sort排序,包括了详解Java中Collections.sort排序的使用技巧和注意事项,需要的朋友参考一下 Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能;如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f,g这样,当然数字也是这样的。 com