2019独角兽企业重金招聘Python工程师标准>>>

背景

最近,项目正在集成测试阶段,项目在服务器上运行了一段时间,点击表格的列进行排序的时候,有的列排序正常,有的列在排序的时候,在后台会抛出如下异常,查询到不到数据,而且在另外一台服务器上是可以排序的, 就是说该问题是偶现的;

java.lang.IllegalArgumentException: Comparison method violates its general contract!at java.util.TimSort.mergeLo(TimSort.java:747)at java.util.TimSort.mergeAt(TimSort.java:483)at java.util.TimSort.mergeCollapse(TimSort.java:410)at java.util.TimSort.sort(TimSort.java:214)at java.util.TimSort.sort(TimSort.java:173)at java.util.Arrays.sort(Arrays.java:659)at java.util.Collections.sort(Collections.java:217)

google之后,发现有很多的人也遇到了这个问题,原因就是 JDK 升级的问题,也才想起来最近项目也升级了JDK, 从 JDK1.6 改为JDK1.7了,

原因

在 JDK1.6 和 JDK1.7 的 Collections.sort(List, Comparator) 方法中,底层的实现变了,在使用Comparator比较器进行比较的时候,可能会返回-1,1和0三种结果,但在平时的时候,往往会忽略0,也就是两个元素相等的情况或者NULL的情况,如果在调用Comparator比较的时候,只会返回-1或1,则在 JDK1.6的时候,可以正常运行,在升级到 JDK1.7之后,有可能会运行失败,会抛出异常:java.lang.IllegalArgumentException: Comparison method violates its general contract!,

在项目的对应代码中也发现了如下代码:

    Collections.sort(aObjectList, new Comparator(){public int compare(Object o1, Object o2){......int reverse = 1;if ("desc".equalsIgnoreCase(sortType)) {reverse = -reverse;}......double result = filedValue1.doubleValue() - filedValue2.doubleValue();int returnValue = 0;if (result > 0.0D) {returnValue = 1;} else if (result < 0.0D) {returnValue = -1;}return returnValue * reverse;......}}

该代码也只会返回 -1 或 1,没有返回 0  的情况,如果两个相等的时候,在 JDK1.7 环境中运行就会出现上述异常了。

JDK1.6 Collections.sort(List, Comparator)方法的实现:

    public static <T> void sort(List<T> list, Comparator<? super T> c) {Object[] a = list.toArray();Arrays.sort(a, (Comparator)c);ListIterator i = list.listIterator();for (int j=0; j<a.length; j++) {i.next();i.set(a[j]);}}public static <T> void sort(T[] a, Comparator<? super T> c) {T[] aux = (T[])a.clone();if (c==null)mergeSort(aux, a, 0, a.length, 0);elsemergeSort(aux, a, 0, a.length, 0, c);}

可以看到使用的是归并排序mertgeSort进行排序,

JDK1.7 Collections.sort(List, Comparator)方法的实现:

    public static <T> void sort(List<T> list, Comparator<? super T> c) {Object[] a = list.toArray();Arrays.sort(a, (Comparator)c);ListIterator i = list.listIterator();for (int j=0; j<a.length; j++) {i.next();i.set(a[j]);}}// Arrays.sort(a, c);public static <T> void sort(T[] a, Comparator<? super T> c) {if(LegacyMergeSort.userRequested){legacyMergeSort(a,c)}else{TimSort.sort(a,c)}|public static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {T[] aux = (T[])a.clone();if (c==null)mergeSort(aux, a, 0, a.length, 0);elsemergeSort(aux, a, 0, a.length, 0, c);}

可以看到它会用 TimSort.sort()方法进行排序,且在 JDK1.7后默认的排序方法。

解决方法

既然在 sort()方法中使用 LegacyMergeSort.userRequested 参数来控制使用 mergeSort排序算法还是使用 TimSort 排序算法,那么只要 LegacyMergeSort.userRequested 为true就会使用mergeSort算法,就会兼容 JDK1.6 的Collections.sort() 方法了,好在 JVM 提供了一个参数来控制:

-Djava.util.Arrays.useLegacyMergeSort=true

所在就在出问题的那台服务器上的虚拟机中添加了该参数,再进行排序的时候,就可以正常排序了,不会再抛出该异常了。

接下来在看一下 LegacyMergeSort.userRequested 是怎么获取的:

    static final class LegacyMergeSort {private static final boolean userRequested =java.security.AccessController.doPrivileged(new sun.security.action.GetBooleanAction("java.util.Arrays.useLegacyMergeSort")).booleanValue();}

可以看到也是根据 java.util.Arrays.useLegacyMergeSort 的值来控制的,所以按理说也可以通过设置该环境变量也实现同样的效果,但是我通过该方法并没有成功,最终在用添加虚拟机参数的方式进行解决。

System.setProperty("java.util.Arrays.useLegacyMergeSort", true)

以上两种方法只能是用来规避异常,让代码正常运行,是一种规避手段,而正确的做法应该是在写代码的过程中,在使用Collections.sort(List, Comparator)来排序的时候,尽量考虑周全,不要漏掉 0 的情况:

       Collections.sort(list, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {if (flag) {return -1;}else if(flag) {return 1;}else{return 0;}}});

或者直接使用 compareTo 方法进行比较:

        Collections.sort(list, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1.compareTo(o2);}});

题外话:TimSort.sort

在 JDK 的源码中 Collections.sort(List, Comparator)方法底层就使用的是归并算法来进行排序的,而在 JDK1.7后,就会使用 TimSort 来进行排序了,而TimSort是归并排序的一种变种,在大部分情况下,TimSort算法的效率较高,且它是稳定,因为在大部分情况下,要排序的集合都是部分有序的,完全无序的集合很少出现,TimSort 就抓住这个规律而对归并排序进行优化的。

TimSort.sort的基本思想:

在要排序的集合中,把部分有序的那些元素找出来,这些部分有序的元素姑且称为一个区域,最终这个集合会被划分为若干个区域,之后把这些区域压入栈中,在栈中对这些区域按照一定的规则进行合并。

具体参考:维基百科

转载于:https://my.oschina.net/mengyuankan/blog/1834497

JDK1.6和JDK1.7中,Collections.sort的区别,相关推荐

  1. java中Collections.sort() 排序函数的用法

    java中Collections.sort() 排序函数的用法: 用Collections.sort方法对list排序有两种方法 第一种是list中的对象实现Comparable接口,如下: /** ...

  2. Java中Collections.sort()的使用!

    请注明出处:http://blog.csdn.net/qq_23179075/article/details/78753136 Java中Collections.sort()的使用! 在日常开发中,很 ...

  3. java中Collections.sort排序详解

    Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能:如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f, ...

  4. Java中Collections.sort()排序详解

    https://www.cnblogs.com/learnapi/p/9003112.html

  5. Collections.sort()自定义排序方式

    Java中Collections.sort()的使用! 在日常开发中,很多时候都需要对一些数据进行排序的操作.然而那些数据一般都是放在一个集合中如:Map ,Set ,List 等集合中.他们都提共了 ...

  6. ht-8 对arrayList中的自定义对象排序( Collections.sort(ListT list, Comparator? super T c))...

    1 package com.iotek.set; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import ja ...

  7. Java8 Collections.sort()及Arrays.sort()中Lambda表达式及增强版Comparator的使用

    摘要:本文主要介绍Java8 中Arrays.sort()及Collections.sort()中Lambda表达式及增强版Comparator的使用. 不废话直接上代码 import com.goo ...

  8. JDK1.7和JDK1.8中HashMap是线程不安全的,并发容器ConcurrentHashMap模型

    一.HashMap是线程不安全的 前言 只要是对于集合有一定了解的一定都知道HashMap是线程不安全的,我们应该使用ConcurrentHashMap.但是为什么HashMap是线程不安全的呢,之前 ...

  9. java中Collections常用方法总结(包括sort,copy,reverse等)

    1.sort(Collection)方法的使用(含义:对集合进行排序). 例:对已知集合c进行排序public class Practice {public static void main(Stri ...

  10. java集合中中文排序_利用Collator和Collections.sort对list进行中文排序,注意与Arrays.sort的区别...

    //两者的关系:1.Coollections.sort的内部实现是用Arrays.sort来实现的. //2.如果要排序的list中的对象已经实现了Comparable接口,那么可以用Arrays.s ...

最新文章

  1. 写得不错的几篇C/C++博客
  2. 李嘉骐:03 PyTorch模块与基础实战
  3. 海思移植opencv+车辆检测
  4. Algorithm——1.排序.md
  5. python策略模式包含角色_详解Python设计模式之策略模式
  6. ES6 学习笔记(一)let,const和解构赋值
  7. 2014年值得关注的10个开源项目 上
  8. Android自定义view之ViewPager指示器——1
  9. apache+mysql+php的环境配置
  10. Eclipse环境安装Python插件PyDev
  11. Ubuntu20.04安装OpenCV3.4.15
  12. python数据挖掘实验报告_数据挖掘实习报告
  13. win10误删的注册表能还原吗_win10电脑注册表修改后如何恢复
  14. DCDC和LDO介绍
  15. 《Thinking in UML》学习1——参与者与用例
  16. 小ck活动机器人包包_古力娜扎空降“小ck”线下门店,手上的包包亮了,仙气又便宜!...
  17. 负载均衡之TCP连接复用与缓冲
  18. 无线覆盖商场微信吸粉解决方案
  19. FFmpeg源码分析:内存管理系统
  20. 机械革命台式计算机,机械革命台式机怎么样

热门文章

  1. 廖雪峰Java2面向对象编程-5包和classpath-4classpath和jar
  2. get------引用接口
  3. 精通 JS正则表达式(转)
  4. emacs,objective-c mode 代码补全!
  5. iOS/Android React Native 配置教程
  6. susmote个人网站博客论坛(TexTec | 关注互联网技术,传播极客精神)
  7. 2011最赚钱的行业和公司排行榜(verified 版本)
  8. 【Zend Studio】10.6.0版本设置默认编码为UTF-8
  9. Linux下编写 makefile 详细教程
  10. Struts2学习笔记(十) OGNL