文档章节

JDK1.6和JDK1.7中,Collections.sort的区别,

tsmyk0715
 tsmyk0715
发布于 06/23 16:04
字数 1316
阅读 26
收藏 0
点赞 0
评论 0
JDK

背景

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

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);
        else
            mergeSort(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);
        else
            mergeSort(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>() {
			@Override
			public 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>() {
			@Override
			public 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的基本思想:

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

具体参考:维基百科

 

© 著作权归作者所有

共有 人打赏支持
tsmyk0715
粉丝 11
博文 36
码字总数 76274
作品 0
成都
程序员
JDK7-Collections.sort()报Comparison method violates its general contract异常

记录在 JDK7 下使用 Collections.sort() 排序偶发的一个异常,以前在 JDK1.6 下面没有这个问题: Comparison method violates its general contract 发生问题的代码段: 一个很简单的排序,在...

山哥
2016/09/12
119
0
java 关于string类的intern方法

0.引言 什么都先不说,先看下面这个引入的例子: [java] view plain copy String str1 = new String("SEU")+ new String("Calvin"); System.out.println(str1.intern() == str1); System.ou......

hgqxjj
2017/12/21
0
0
java项目发布到sae上遇到的问题汇总

写在最前面:sae新浪云相比交bae还是有诸多限制的。比如jdk版本,对java许多框架支持不够,但并不影响其使用。现就本人上传到sae的项目遇到的问题进行汇总分析。 问题1: 查看日志后主要报错...

传奇再现
2014/05/07
0
0
JDK7和JDK6中substring()的不同

String substring(int beginIndex, int endIndex) 返回原字符串的子字符串这方法,只要是稍微了解点java的人都知道,就像知道1+1==2一样简单。不过其中的猫腻很少有人关注,就像基本没人问1...

浪子_仗剑走天涯
2013/11/21
0
9
Comparable与Comparator的区别

前几天在项目中遇到了一个将复杂对象进行排序的问题:计算BingMap地图上距离当前位置5KM内发生事故(TrafficIncident)的点到当前位置的距离,并按距离升序排序。距离都算出来了,但这些Tra...

摆渡者
2014/04/12
0
0
jdk1.6 1.7 list扩容的区别

jdk1.6 public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCa......

小艺术家被占用了
2016/08/16
19
0
dubbo典型协议、传输组件、序列化方式组合性能对比测试

前言 Dubbo作为一个扩展能力极强的分布式服务框架,在实现rpc特性的时候,给传输协议、传输框架和序列化方式提供了多种扩展实现,供开发者根据实际场景进行选择。 1、支持常见的传输协议:R...

杨武兵
2016/06/13
2.6K
6
如何让你mac osx的eclipse中debug JDK1.7时显示本地变量

自从买了MAC Air后,对普通的windows电脑越来越看不顺眼了. Mac OSX的强大人性化功能,安全性,配置简单,漂亮的图形界面, 一切都是windows所不能比的. 在Mac的历史上,乔布斯被驱逐出apple公司,...

xpbug
2012/12/22
0
2
linux下面搭建jenkins服务器

1.去官网下载jenkins.war包 下载的时候,注意了,如果下载现在最新的jenkins包的话则要用jdk1.7的版本,我是为了配合项目,我们项目里面使用就是jdk1.6,所以不想再用一个jdk1.7,就直接用项目...

双月通天
2016/06/20
62
0
lucene从哪个版本开始基于JDK1.7进行开发

最近在学习lucene,下载的最新版4.10.3准备学习,下载源码下来看发现他是基于JDK 1.7版本的,但是翻看lucene的历史版本似乎没找到从哪个版本开始使用JDK1.7的。 只是觉得目前大部分公司应该还...

haffun
2015/01/24
2.1K
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring Cloud Gateway 接口文档聚合实现

pigX:码云地址:最新的Spring Cloud 技术栈,Spring Cloud Finchley、oAuth2的最佳实践 在微服务架构下,通常每个微服务都会使用Swagger来管理我们的接口文档,当微服务越来越多,接口查找管...

冷冷gg
6分钟前
6
0
流利阅读笔记30-20180719待学习

1.今日导读 2.带着问题听讲解 3.新闻正文(中英文对照) 4.重点词汇 5.拓展内容

aibinxiao
8分钟前
1
0
OSChina 周五乱弹 —— 我们是食物链的最底层

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @温家成 :分享谢安琪的单曲《姿色份子》 《姿色份子》- 谢安琪 手机党少年们想听歌,请使劲儿戳(这里) @贪吃飒:最近p2p怎么了、半个月爆了...

小小编辑
22分钟前
5
1
Android Studio 3.0 之后打包apk出现应用未安装问题

1、废话 出现这个问题的原因,并不是只有一个,而是有多个原因,不懂的估计会被搞得一头雾水,下面我列举的是我遇到的几种问题和网友遇到的几种问题,但不一定是全部,也有可能有些莫名其妙的...

她叫我小渝
41分钟前
0
0
前端基础

1. get请求传参长度的误区 误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。 实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是...

wenxingjun
今天
0
0
拦截SQLSERVER的SSL加密通道替换传输过程中的用户名密码实现运维审计(一)

工作准备 •一台SQLSERVER 2005/SQLSERVER 2008服务 •SQLSERVER jdbc驱动程序 •Java开发环境eclipse + jdk1.8 •java反编译工具JD-Core 反编译JDBC分析SQLSERVER客户端与服务器通信原理 SQ...

紅顏為君笑
今天
9
0
jQuery零基础入门——(六)修改DOM结构

《jQuery零基础入门》系列博文是在廖雪峰老师的博文基础上,可能补充了个人的理解和日常遇到的点,用我的理解表述出来,主干出处来自廖雪峰老师的技术分享。 在《零基础入门JavaScript》的时...

JandenMa
今天
0
0
linux mint 1.9 qq 安装

转: https://www.jianshu.com/p/cdc3d03c144d 1. 下载 qq 轻聊版,可在百度搜索后下载 QQ7.9Light.exe 2. 去wine的官网(https://wiki.winehq.org/Ubuntu) 安装 wine . 提醒网页可以切换成中...

Canaan_
今天
0
0
PHP后台运行命令并管理运行程序

php后台运行命令并管理后台运行程序 class ProcessModel{ private $pid; private $command; private $resultToFile = ''; public function __construct($cl=false){......

colin_86
今天
1
0
数据结构与算法4

在此程序中,HighArray类中的find()方法用数据项的值作为参数传递,它的返回值决定是否找到此数据项。 insert()方法向数组下一个空位置放置一个新的数据项。一个名为nElems的字段跟踪记录着...

沉迷于编程的小菜菜
今天
1
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部