天天看点

比较器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。