场景
今天在安安心心的敲着代码,安逸的思考着业务逻辑,突然就被测试的紧急呼叫给打破,原来是客户现场的生产环境出现问题了,通过日志的定位才发现是排序的问题。明明代码上线很久了,通过提交记录也没有发现有相关代码的修改,那到底是什么情况呢?通过下面我们来了解一下。
异常描述
java.lang.IllegalArgumentException: Comparison method violates its general contract!
异常出现情况
Collections.sort(list, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
Collections.sort(list, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() > o2.getAge() ? 1 : -1;
}
} );
原因分析
JDK7及以上版本中的sort函数实现变了,与旧版本的不同,存在兼容问题了,导致有些使用旧版本的写法可能会出现问题。在开发过程或者测试过程当中没有出现问题,但是到了生产环境有可能就会出现报错。
当x == y时,sgn(compare(x, y)) = -1,-sgn(compare(y, x)) = 1,这违背了sgn(compare(x, y)) == -sgn(compare(y, x))约束
新版本中对比较有约束条件,如果违反了约束条件就会抛出llegalArgumentException异常,JDK6中则忽略了这种情况,那么新的约束是啥子?
sgn(compare(x, y)) == -sgn(compare(y, x))
((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0
compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z
所以上面的代码就存在问题,运行过程当中可能就会出现这个报错。大家一定要注意啊,这个写法出现问题,并不一定就会出现报错,不能以报错来判别代码的写法是否有问题,有可能在开发测试甚至是生产上运行都一直没有问题,然后突然一天就出现问题了,并且错误还很难进行复现。
解决办法
- 增加jvm启动参数 -Djava.util.Arrays.useLegacyMergeSort=true
- 修改排序写法
Collections.sort(list, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
int i = o1.getAge().compareTo(o2.getAge());
return -i;
}
});
Collections.sort(list, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() >= o2.getAge() ? 1 : -1;
}
} );
结语
大家平时在开发过程当中一定要注意这个问题,因为这个问题并不一定会暴露在你的开发和测试过程当中,等部署在生产的时候,就等于埋了一个雷,指不定哪天就暴雷了。