jdk7 Collections.sort()方法报非法参数异常
JDK7的Comparison method violates its general contract异常
前一阵遇到了一个使用Collections.sort()时报异常的问题,跟小伙伴@zhuidawugui 一起排查了一下,发现问题的原因是JDK7的排序实现改为了TimSort,之后我们又进一步研究了一下这个神奇的算法。
2.背景
先说一下为什么要研究这个异常,前几天线上服务器发现日志里有偶发的异常
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:868)
at java.util.TimSort.mergeAt(TimSort.java:485)
at java.util.TimSort.mergeCollapse(TimSort.java:408)
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)
...
出错部分的代码如下:
修改为如下:
解决方案
先说如何解决,解决方式有两种。
修改代码
上面代码写的本身就有问题,第4行没有考虑o1 == o2的情况,再者说我们不需要自己去比较,修改为如下代码即可:
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// return o1 > o2 ? 1 : -1;
return o1.compareTo(o2);// 正确的方式
}
});
不修改代码
那么问题来了。为什么上面代码在JDK6中运行无问题,而在JDK7中却会抛异常呢?这是因为JDK7底层的排序算法换了,如果要继续使用JDK6的排序算法,可以在JVM的启动参数中加入如下参数:
[plain] view plain copy print?在CODE上查看代码片派生到我的代码片
-Djava.util.Arrays.useLegacyMergeSort=true
这样就会照旧使用JDK6的排序算法,在不能修改代码的情况下,解决这个兼容的问题。
扩展阅读:http://blog.csdn.net/ghsau/article/details/42012365
原因剖析:
google了一下:JDK7中的Collections.Sort方法实现中,如果两个值是相等的,那么compare方法需要返回0,否则可能会在排序时抛错,而JDK6是没有这个限制的。
这个问题在测试时并没有出现,线上也只是小概率复现,如何稳定的复现这个问题?看了一下源代码,抛出异常的那段源代码让人根本摸不着头脑:
if (len2 == 0) {
throw new IllegalArgumentException("Comparison method violates its general contract!");
}
if (len2 == 0) {
throw new IllegalArgumentException("Comparison method violates its general contract!");
}
为了解开这个困惑,我们对java实现的Timsort代码做了一些分析。
更多关于Timsort概述:
http://blog.2baxb.me/archives/993
前一阵遇到了一个使用Collections.sort()时报异常的问题,跟小伙伴@zhuidawugui 一起排查了一下,发现问题的原因是JDK7的排序实现改为了TimSort,之后我们又进一步研究了一下这个神奇的算法。
2.背景
先说一下为什么要研究这个异常,前几天线上服务器发现日志里有偶发的异常
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:868)
at java.util.TimSort.mergeAt(TimSort.java:485)
at java.util.TimSort.mergeCollapse(TimSort.java:408)
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)
...
出错部分的代码如下:
public static List<Map.Entry<String, Integer>> hashSort() { // System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); List<Map.Entry<String, Integer>> list_Data = new ArrayList<Map.Entry<String, Integer>>(dataHash.entrySet()); Collections.sort(list_Data, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { if (o2.getValue() != null && o1.getValue() != null && o2.getValue().compareTo(o1.getValue()) > 0) { return 1; } else{ return -1; } } }); return list_Data; }
修改为如下:
public static List<Map.Entry<String, Integer>> hashSort() { // System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); List<Map.Entry<String, Integer>> list_Data = new ArrayList<Map.Entry<String, Integer>>(dataHash.entrySet()); Collections.sort(list_Data, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { if (o2.getValue() != null && o1.getValue() != null && o2.getValue().compareTo(o1.getValue()) > 0) { return 1; } else if(null == o1.getValue()){ return -1; }else if(null == o2.getValue()){ return 1; } return Float.compare(o1.getValue(),o1.getValue()); } }); return list_Data; }
解决方案
先说如何解决,解决方式有两种。
修改代码
上面代码写的本身就有问题,第4行没有考虑o1 == o2的情况,再者说我们不需要自己去比较,修改为如下代码即可:
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// return o1 > o2 ? 1 : -1;
return o1.compareTo(o2);// 正确的方式
}
});
不修改代码
那么问题来了。为什么上面代码在JDK6中运行无问题,而在JDK7中却会抛异常呢?这是因为JDK7底层的排序算法换了,如果要继续使用JDK6的排序算法,可以在JVM的启动参数中加入如下参数:
[plain] view plain copy print?在CODE上查看代码片派生到我的代码片
-Djava.util.Arrays.useLegacyMergeSort=true
这样就会照旧使用JDK6的排序算法,在不能修改代码的情况下,解决这个兼容的问题。
扩展阅读:http://blog.csdn.net/ghsau/article/details/42012365
原因剖析:
google了一下:JDK7中的Collections.Sort方法实现中,如果两个值是相等的,那么compare方法需要返回0,否则可能会在排序时抛错,而JDK6是没有这个限制的。
这个问题在测试时并没有出现,线上也只是小概率复现,如何稳定的复现这个问题?看了一下源代码,抛出异常的那段源代码让人根本摸不着头脑:
if (len2 == 0) {
throw new IllegalArgumentException("Comparison method violates its general contract!");
}
if (len2 == 0) {
throw new IllegalArgumentException("Comparison method violates its general contract!");
}
为了解开这个困惑,我们对java实现的Timsort代码做了一些分析。
更多关于Timsort概述:
http://blog.2baxb.me/archives/993
发表评论
-
关系型数据库三范式解释
2016-04-07 11:54 1232数据库 三范式最简单最易记的解释,整理一下方便大家记忆。 书上 ... -
java验证字符串中是否包含数字,对数字的操作
2016-03-15 11:01 9479在javascript中有一个方法 ... -
Maven 中央仓库地址和lastUpdate文件删除
2016-03-01 13:46 8033Maven 中央仓库地址: 1. http://mvnrep ... -
log4j.properties配置详解
2016-01-18 16:50 1271Log4J的配置文件(Configuration File)就 ... -
Java 日期时间 Date类型,long类型,String类型表现形式的转换
2015-12-24 17:35 3269Java 日期时间 Date类型,long类型,String类 ... -
Java多线程-工具篇-BlockingQueue
2015-11-24 16:13 972Java多线程-工具篇-Block ... -
Java 实例 - 队列(Queue)入门用法
2015-11-23 17:27 1995队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表 ... -
Java中序列化的serialVersionUID作用
2015-11-13 14:13 4808Java序列化是将一个对象 ... -
java的序列化和反序列化
2015-10-27 19:48 1109Java基础学习总结——Jav ... -
java中volatile关键字的含义
2015-08-12 20:10 748java中volatile关键字的含 ... -
Java读写文件中文乱码问题
2015-07-20 17:49 2956问题:在用Java程序进行读写含中文的txt文件时,经常会出现 ... -
String类中split方法的使用
2015-07-02 14:39 979String类中split方法的使用 split 方法:将一个 ... -
PreparedStatement防止SQL注入
2015-04-11 16:27 2786一条效率差的sql语句,足以毁掉整个应用. Stateme ... -
Session的生命周期
2015-04-11 11:58 904我们已经知道,Session是在用户第一次访问网 ... -
JAVA多线程和并发基础
2015-04-11 11:58 765JAVA多线程和并发基础 ... -
Java中equals()与hashCode()方法详解
2015-04-08 16:19 872一.equals()方法详解 equals()方法在o ... -
json数据后台处理
2015-03-30 16:05 789JAVA解析JSON问题,怎么解析,急!! String j ... -
java判断list为空
2015-01-30 15:25 3937java判断list为空 if(null == list | ... -
httpclient使用实践
2015-01-09 17:09 1333httpclient是什么这里不再详述(可参考最下方网址);直 ... -
java反射获取属性和方法
2015-01-09 11:12 3102反射的应用一般是要用到某些特殊类的属性和方法,无论是一般方法还 ...
相关推荐
JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip JDK7安装包.zip\JDK7安装包.zip\JDK7安装包.zip\JDK7...
jdk-11.0.8.jdk.zip
JDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0.1310.11_32bitJDK_8.0....
最新版windows jdk-11.0.18_windows-x64_bin.zip最新版windows jdk-11.0.18_windows-x64_bin.zip
最新版windows jdk-11.0.20_windows-x64_bin.exe最新版windows jdk-11.0.20_windows-x64_bin.exe最新版windows jdk-11.0.20_windows-x64_bin.exe
最新版linux jdk-11.0.20_linux-x64_bin.tar.gz最新版linux jdk-11.0.20_linux-x64_bin.tar.gz最新版linux jdk-11.0.20_linux-x64_bin.tar.gz
JDK安装包。在XP下开发。不依赖工具。版本jdk_8.0.1310.11_64
Java API 文档 jdk-17.0.2_doc-all
最新版linux jdk-11.0.12_linux-x64_bin.tar.gz最新版linux jdk-11.0.12_linux-x64_bin.tar.gz
JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具. JDK是学好Java的第一步。不管是你要学习java...
最新官方jdk-11.0.15_windows-x64_bin.zip, 直接解压即可使用。
JDK 17.0.7 的主要特点 支持 Java 应用程序的跨平台性:JDK 17.0.7 支持 Windows、Linux 和 macOS 等操作系统,使开发人员能够在同一平台上开发并运行 Java 应用程序。 改进的性能和稳定性:JDK 17.0.7 包括许多性能...
jdk_8.0.1310.11_64.exe -64位 可用! !!!!!!!
最新版linux jdk-11.0.16.1_linux-x64_bin.tar.gz最新版linux jdk-11.0.16.1_linux-x64_bin.tar.gz
最新版linux jdk-11.0.13_linux-x64_bin.tar.gz最新版linux jdk-11.0.13_linux-x64_bin.tar.gz
最新版linux jdk-11.0.8_linux-x64_bin.tar.gz
jdk-11.0.10.jdk.zip
jdk 1.8.tar.gz
JDK tools.jar
jdk-11.0.12_windows-x64_bin.exe 压缩包