天天看點

比較器Comparator使用

最近在項目中做視訊搜尋功能,在獲得視訊結果集後需要對視訊清單根據集數做排序,自然而然想到了用jdk的比較器Comparator,

編寫代碼實作如下

private void sortResItem(List<SearchVideoDTO> videos, Boolean isDownSort) {
    //集數排序
    if (!CollectionUtils.isEmpty(videos)) {
        Collections.sort(videos, new Comparator<SearchVideoDTO>() {
            public int compare(SearchVideoDTO o1, SearchVideoDTO o2) {
                if(JudgeUtils.isNotNull(o2.getEpisode()) && JudgeUtils.isNotNull(o1.getEpisode())){
                    if (isDownSort) {
                        return o2.getEpisode() - o1.getEpisode();
                    } else {
                        return o1.getEpisode() - o2.getEpisode();
                    }
                }else {
                    return 0;
                }
            }
        });
    }
}
           

看着邏輯沒毛病,但是在運作時卻出現報錯 java.lang.IllegalArgumentException: Comparison method violates its general contract!

網上查閱相關資料介紹使用Comparator要滿足三個特性

1) 自反性:x,y 的比較結果和 y,x 的比較結果相反。

2) 傳遞性:x>y,y>z,則 x>z。

3) 對稱性:x=y,則 x,z 比較結果和 y,z 比較結果相同。

https://www.cnblogs.com/wendelhuang/p/7356797.html

光看這3個特性了解起來比較抽象,通過一個流程圖看就比較直覺

比較器Comparator使用

由流程圖推導過程可知,如果代碼邏輯滿足傳遞性則可以推導出b=c的結論,但是實際比較結果是b<c,是以這種代碼實作邏輯不能滿足前面提到的傳遞性。将上面的代碼稍作修改

private void sortResItem(List<SearchVideoDTO> videos, Boolean isDownSort) {
    //集數排序
    if (!CollectionUtils.isEmpty(videos)) {
        Collections.sort(videos, new Comparator<SearchVideoDTO>() {
            public int compare(SearchVideoDTO o1, SearchVideoDTO o2) {
                Integer a1 = o1.getEpisode() == null ? 0 : o1.getEpisode();
                Integer a2 = o2.getEpisode() == null ? 0 : o2.getEpisode();
                if(isDownSort) {
                    return a2-a1;
                }else {
                    return a1-a2;
                }
            }
        });
    }
}
           

報錯解決,而且函數傳回符合預期結果。

問題分析:第一種寫法的思維誤區是認為compare函數單純比較兩個對象的大小,傳回兩個值的比較結果即可,但實際情況是參與比較的對象不隻是參與一次比較,在一個元素數量大于2的集合清單中,每個元素至少要參與兩次比較才能确定在集合中的排序位置,如果參與比較的對象比較字段值為Null的時候,會因為不滿足前面提到的三個特性報錯。

總結:使用Comparator簡單的處理辦法就是将每個參與比較的對象都對應一個可以比較大小的确定的值,正如第二種寫法,如果參與比較對象的比較字段為Null可以把比較的值賦為0。