`

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)
...
出错部分的代码如下:   
 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
分享到:
评论

相关推荐

    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安装包.zip\JDK7...

    最新官方jdk-11.0.15_windows-x64_bin

    标题“最新官方jdk-11.0.15_windows-x64_bin”指的是Java Development Kit (JDK) 的一个特定版本,即11.0.15,它针对Windows操作系统且为64位架构设计。这个JDK是官方发布的,确保了可靠性和安全性。 描述中的...

    最新版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最新版windows jdk-11.0.20_windows-x64_bin.exe

    jdk1.8 64位 jdk-8.0.1310.11-64

    7. **默认方法**:在接口中定义的非抽象方法具有默认实现,这允许在不破坏向后兼容性的情况下向接口添加新功能。 8. **类型注解**:Java 8增加了类型注解的使用范围,可以用于泛型参数、返回类型、方法参数以及局部...

    jdk_8.0.1310.11_64.exe -64位

    jdk_8.0.1310.11_64.exe -64位 可用! !!!!!!!

    jdk-11.0.14_windows-x64_bin.exe

    4. 新的字符串方法:JDK 11添加了一些新的字符串处理方法,如`isBlank()`、`strip()`和`stripIndent()`,简化了日常编码工作。 5. 改进的垃圾收集器:JDK 11优化了G1垃圾收集器,提升了性能,并引入了ZGC(Z ...

    JDK_8.0.1310.11_32bit

    7. **Optional类**:为了更好地处理可能为空的对象值,引入了`Optional`类,避免空指针异常的问题。 8. **Reactive Streams**:提供了非阻塞流处理的支持,适用于高并发场景。 ### 三、32位与64位JDK的区别 JDK...

    JDK1.8下载 : jdk_8.0.1310.11_64.zip

    JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具. JDK是学好Java的第一步。不管是你要学习java...

    JDK_8.0.1310.11_32bit.rar

    7. **Optional类**:`Optional&lt;T&gt;`是一个容器类,代表一个值存在或不存在,避免了空指针异常,提高了代码的健壮性。 8. **类型注解**:Java 8增强了类型注解的使用,允许在更多位置使用注解,如类型参数、返回类型...

    Java-JDK-11.0.8(Windows &amp;amp; Mac os) 下载

    Java JDK 11.0.8 是Oracle公司发布的Java开发工具包的一个稳定版本,它针对开发者提供了完整的编译、调试和运行Java应用程序所需的环境。这个版本支持Windows和Mac OS操作系统,使得不同平台上的开发者都能方便地...

    JDK1.8.0.221 .zip

    此外,还引入了方法引用来代替传统的匿名内部类,提高了代码的可读性。Java 8还引入了Stream API,这是一个用于处理集合的新概念,可以实现更高效的聚合操作,如过滤、映射和规约等。 在JDK 1.8.0.221这个特定版本...

    jdk-11.0.11_windows-x64_bin.exe

    7. **增强的ZGC**:Z Garbage Collector在JDK 11中得到改进,对于大内存应用,它能提供更低的暂停时间和更高的吞吐量。 8. **JEP 320:移除Java EE和Corba模块**:为了简化JDK,不再包含Java EE和Corba相关的模块,...

    Java API 文档 jdk-17.0.2-doc-all

    7. **反射机制**:`java.lang.reflect`包提供了反射API,允许程序在运行时动态访问和修改类、接口、字段和方法,增强了代码的灵活性。 8. **泛型**:Java 5引入了泛型,提高了类型安全性和代码复用。`&lt;T&gt;`等通配符...

    jdk1.7 tools.jar

    当想用cmd运行java文件时,出现问题。可能是因为jdk\lib 文件夹缺少tools.jar

    JDK tools.jar

    JDK tools.jar

    jdk1.8 rt.jar 源码

    7. **日期与时间API**:在JDK 1.8中,`java.time`包提供了新的日期和时间API,如`LocalDate`、`LocalTime`和`ZonedDateTime`,比旧的`java.util.Date`和`Calendar`更易用且功能强大。 通过深入研究`rt.jar`源码,...

    jdk1.8.tar.gz

    7. **默认方法**:在接口中,Java 8允许定义具有实现的方法,称为默认方法,这允许在不破坏向后兼容性的情况下向接口添加新方法。 8. **Nashorn JavaScript引擎**:Java 8引入了一个内置的JavaScript引擎Nashorn,...

    jdk-11.0.1.zip

    **Java Development Kit (JDK) 11.0.1** JDK 11.0.1 是一个重要的Java开发工具包,它包含了Java运行时环境(JRE)以及用于编译、调试和运行Java应用程序所需的工具。这个版本是Oracle公司发布的Java SE(Standard ...

    jdk api 1.8.rar

    在JDK 1.8中,接口可以定义默认方法,这些方法有实现体,可以在不打破现有实现的情况下向接口添加新方法。这使得接口可以扩展而不会破坏已经实现该接口的类。例如: ```java public interface MyInterface { ...

    jdk_1.8.1310.11_x64安装包

    它们允许将函数作为参数传递给方法,或者将代码视为数据进行操作。 2. **方法引用来替代匿名内部类**:在Java 8中,你可以直接引用一个静态方法或实例方法,而无需创建匿名内部类。 3. **默认方法**:接口中可以...

Global site tag (gtag.js) - Google Analytics