记录下以前调用Collections.sort()形成App Crash的例子。业务缘由,须要在主App中的文件进行排序,排序的规则是按照最近的修改时间升序排序,而后删除修改时间较小的文件列表,实现简单的清缓存功能。可是简单的实现后,注解抛出一个java.lang.IllegalArgumentException:Comparison method violates its general contract!。java
哈哈 让咱们一块儿摇摆!算法
问题代码以下:缓存
Collections.sort(child, new Comparator<File>() {
@Override
public int compare(File lFile, File rFile) {
Long lModified = lFile.lastModified();
Long rModified = rFile.lastModified();
return lModified.compareTo(rModified);
}
});
复制代码
是否是乍一看,以为代码写的丝毫问题都没有,是的,刚开始我也是这么以为。可是这篇文章你接着往下看,就知道哪里出了问题了。ide
说到Collections.sort()和java.lang.IllegalArgumentException:Comparison method violates its general contract!这个崩溃,相信你们都已经百度过大概由于什么缘由了。没错,Collections.sort()在JDK6和JDK7中实现的底层排序算法变了,在JDK6中使用的时MergeSort排序,而在JDK7中使用的是TimSort。里面具体的算法自行百度吧,我是实在没看懂里面咋实现的,可是这个传说中的TimSort排序算法对比较大小的要求更高了:spa
比较器Comparator要求:code
1 sgn(compare(x, y)) == -sgn(compare(y, x))
2 ((compare(x, y)>0) && (compare(y, z)>0))
3 若是compare(x, y)==0 那么sgn(compare(x, z))==sgn(compare(y, z))
复制代码
举个例子,好比有下面的代码:cdn
Collections.sort(child, new Comparator<Integer>() {
@Override
public int compare(Integer l, Integer r) {
return l > r ? 1 : -1;
}
});
复制代码
恭喜你,crash了。 为何呢,由于这里面就违反了自反性第一个规则,好比l的值是1,r的值也是1,那么compare(l,r)和compare(r,l)的结果是不同的,因而TimSort就会检测到这种异常,就GG了。排序
可是!可是!it
前面说的这段代码:io
Collections.sort(child, new Comparator<File>() {
@Override
public int compare(File lFile, File rFile) {
Long lModified = lFile.lastModified();
Long rModified = rFile.lastModified();
return lModified.compareTo(rModified);
}
});
复制代码
有什么问题呢?调用的都是SDK内部实现的compareTo,其实吧,这里面确实是没有问题的,可是这里面忽视了一种状况:
File为null的状况!
File为null的状况!
File为null的状况!
那么你就会说,我能够保证这里面的File文件都是非空的啊,个人代码能够保证啊,并且也没有报NullPointException异常呀,为何要考虑File为null的状况呢。
由于缘由很简单,JVM并不知道.就是这么简单粗暴,由于JVM对你的代码是无感知的,它没法感知File文件是否必定非空,因此JVM就会在假设File为null的时候,没法断定比较的正确性,而后抛出异常出来。
知道缘由,那么解决方法就是对File为null或File不存在的状况进行下兼容处理,处理后的代码以下:
Collections.sort(child, new Comparator<File>() {
@Override
public int compare(File lFile, File rFile) {
boolean lInValid = (lFile == null || !lFile.exists());
boolean rInValid = (rFile == null || !rFile.exists());
boolean bothInValid = lInValid && rInValid;
if (bothInValid) {
return 0;
}
if (lInValid) {
return -1;
}
if (rInValid) {
return 1;
}
Long lModified = lFile.lastModified();
Long rModified = rFile.lastModified();
return lModified.compareTo(rModified);
}
});
复制代码
而后问题完美解决。
java.lang.IllegalArgumentException:Comparison method violates its general contract!这个异常确实很坑,在使用Collections.sort排序时,很容易抛异常,因此只能在写里面排序逻辑的时候,当心当心再当心,若是可能的话, 最好使用SDK内部实现的compareTo方法,这样会少不少坑。
转载请标明来源,个人公众号:哈希同窗