今天一个群里哥们儿碰到一个异常,抛到群里求解答,他的代码如下图:
抛出的异常信息为:
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)
我说是 compare 方法实现的问题,他死活跟我掰,说我之前代码还好好的啊。没办法,我只好根据异常信息提示去翻JDK源码,异常里提示 at java.util.TimSort.mergeHi(TimSort.java:868) 即 TimSort 类的 mergeHi 方法抛出的。于是我不断Google,找到了这篇帖子《why does my compare method throw exception — Comparison method violates its general contract》,根据他们的提示,我大概了解了compare方法需要返回1,-1,0即你的返回值要符合约定。
于是我又按照异常提示看了 Collections 的 sort 方法源码,如图:
继续跟踪Arrays类的sort方法:
看到这里我基本就豁然开朗了,因为抛异常的地方是在 TimSort 类里,说明实际走的是 else 分支,所以有了第一种解决方法,添加 -Djava.util.Arrays.useLegacyMergeSort=true 这个 JVM 参数,其实要真正解决这个问题,要符合规范的实现 compare 方法,因为他写的代码里没有考虑对象 o1 和对象 o2 为 Null 的情况,即当 o1 与 o2 都为 null 时两者大小如何判定呢,当 o1 为 null 但 o2 不为 null 时两者大小又如何判定了呢,同理当o2为null但o1不为 null 时两者大小又如何判定呢又不得而知,或许你又会说,我这两个对象不可能为 null,但那是你认为,JVM 不知道,它只要求你的逻辑必须严谨,严格考虑各种情况下两者大小的判定原则。所以正确写法应该是:
if(o1 == null && o2 == null) {
return 0;
}
if(o1 == null) {
return -1;
}
if(o2 == null) {
return 1;
}
if(o1.getCreateTime() > o2.getCreateTime()) {
return 1;
}
if(o2.getCreateTime() > o1.getCreateTime()) {
return -1;
}
return 0;