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

你可能感兴趣的:(method,JDK7,general,comparison,its,Contract,violates)