Comparison method violates its general contract!

背景

16号为了统一线上服务器运行环境,将两台服务器的Tomcat6+JDK6升级到Tomcat7+JDK7,本以为很简单的事情,升级后自己验证也没问题,没想到却悲剧了。升级后,过了半小时运营就找过来反馈问题,部分角色无法登陆系统,由于异常日志没有输出,没有找到问题,无奈回滚。今天我们就来说说JDK6升级到JDK7会遇到的坑。本文为了方便搜索,就直接以异常信息作为文章标题了。

复现

回滚后,到beta环境按照线上的权限配置,复现该问题,加上了error日志输出,输出了文章标题的异常,这个异常是在类似如下代码中抛出的:

Collections.sort(list, new Comparator<Integer>() {
	@Override
	public int compare(Integer o1, Integer o2) {
		return o1 > o2 ? 1 : -1;// 错误的方式
	}
});

解决方案

先说如何解决,解决方式有两种。

修改代码

上面代码写的本身就有问题,第4行没有考虑o1 == o2的情况,再者说我们不需要自己去比较,修改为如下代码即可:

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的启动参数中加入如下参数:

-Djava.util.Arrays.useLegacyMergeSort=true

这样就会照旧使用JDK6的排序算法,在不能修改代码的情况下,解决这个兼容的问题。

分析

在我以前的认知中,高版本的JDK是可以兼容之前的代码的,与同事讨论了一番另加搜索了一番,事实证明,JDK6到JDK7确实存在兼容问题(不兼容列表)。在不兼容列表中我们可以找到关于Collections.sort的不兼容说明,如下:

Area: API: Utilities
Synopsis: Updated sort behavior for Arrays and Collections may throw an IllegalArgumentException
Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. 
The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. 
The previous implementation silently ignored such a situation.
If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, 
to restore previous mergesort behavior.
Nature of Incompatibility: behavioral
RFE: 6804124

描述的意思是说,java.util.Arrays.sort(java.util.Collections.sort调用的也是此方法)方法中的排序算法在JDK7中已经被替换了。如果违法了比较的约束新的排序算法也许会抛出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

再回过头来看我们开篇有问题的实现:

return x > y ? 1 : -1;

当x == y时,sgn(compare(x, y))  = -1,-sgn(compare(y, x)) = 1,这违背了sgn(compare(x, y)) == -sgn(compare(y, x))约束,所以在JDK7中抛出了本文标题的异常。

结论

那么现在是否可以盖棺定论了,按照上面的分析来看,使用这种比较方式(return x > y ? 1 : -1;),只要集合或数组中有相同的元素,就会抛出本文标题的异常。实则不然,什么情况下抛出异常,还取决于JDK7底层排序算法的实现,也就是大名鼎鼎的TimSort。后面文章会分析TimSort。本文给出一个会引发该异常的Case,以便有心人共同研究,如下:

Integer[] array = 
{0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 2, 1, 0, 0, 0, 2, 30, 0, 3};

      (完)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Aloys的开发之路

一个比较全面的java随机数据生成工具包

        最近,由于一个项目的原因需要使用一些随机数据做测试,于是写了一个随机数据生成工具,ExtraRanom。可以看成是Java官方Random类的扩...

1889
来自专栏Python中文社区

3D酷炫立体图现已加入 pyecharts 豪华晚餐

專 欄 ❈陈键冬,Python中文社区专栏作者 GitHub: https://github.com/chenjiandongx ❈ pyecharts项目由...

2235
来自专栏量化投资与机器学习

【干货】Matlab的内存问题讨论

谢谢大家支持,可以让有兴趣的人关注这个公众号。让知识传播的更加富有活力,谢谢各位读者。 很多人问我为什么每次的头像是奥黛丽赫本,我只能说她是我女神,每天看看女神...

1778
来自专栏编程

PHP中性能优化之生成器

问题背景 PHP生成器是PHP的5.5.0版本引入的功能,生成器实际上就是简单的迭代器。生成器会根据需求计算产出迭代的值,而标准的PHP迭代器经常在内存中执行迭...

1939
来自专栏人工智能LeadAI

TensorFlow的Debugger调试实例

之前有翻译整理过关于TensofFlow的Debugger的简单教程,具体内容见这里。这次用自己实际的例子,来简要的做个使用介绍。 首先是代码遇到了问题,训练过...

3256
来自专栏云时之间

深度学习与神经网络:制作数据集,完成应用(1)

在这一篇文章里,我们将继续上一篇文章的工作,并且在上一篇文章的前提下加入数据集的制作,最终我们将完成这个全连接神经网络的小栗子.

4246
来自专栏人工智能LeadAI

TensorFlow的Debugger调试实例

之前有翻译整理过关于TensofFlow的Debugger的简单教程,具体内容见这里(https://www.jianshu.com/p/9fd237c7fda...

3619
来自专栏机器学习算法与Python学习

Python中的实用小技巧

关键字全网搜索最新排名 【机器学习算法】:排名第一 【机器学习】:排名第二 【Python】:排名第三 【算法】:排名第四 话说python是一个大杂会,既可以...

3145
来自专栏编程微刊

NumPy的文件读写

1374
来自专栏北京马哥教育

详解 Python qrcode 二维码模块

1、version:控制二维码的大小,取值范围从1到40。取最小值1时,二维码大小为21*21。取值为 None (默认)或者使用fit=true参数(默认)时...

1000

扫码关注云+社区