Collections.sort()報Comparison method violates its general contract異常問題解決

背景

記錄下以前調用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方法,這樣會少不少坑。

轉載請標明來源,個人公衆號:哈希同窗

相關文章
相關標籤/搜索