Comparator异常:Comparison method violates its general contract!

最近写排序用到的比较器时,碰到了类似如下的异常:

    java.lang.IllegalArgumentException: Comparison method violates its general contract!  
    at java.util.TimSort.mergeHi(TimSort.java:868)  
    at java.util.TimSort.mergeAt(TimSort.java:485)  
    at java.util.TimSort.mergeCollapse(TimSort.java:408)  
    at java.util.TimSort.sort(TimSort.java:214)  
    at java.util.TimSort.sort(TimSort.java:173)  
    at java.util.Arrays.sort(Arrays.java:659)  
    at java.util.Collections.sort(Collections.java:217)  

自己代码大致如下所示,主要功能就是将获取的文件按照时间排序:

public ArrayList getAllLogFiles(int flag) {
    ArrayList fileList = new ArrayList();

    //省略获取File的逻辑
    ........

    if (fileList.size() > 1) {
        //比较器,根据时间决定结果
        Collections.sort(fileList, new Comparator() {
            @Override
            public int compare(File lFile, File rFile) {
                long val = lFile.lastModified() - rFile.lastModified();
                if (val > 0) {
                    return -1;
                } else if (val == 0L) {
                    return 0;
                }
                return 1;
            }
        });
    }

    return fileList;
}

搜索该异常信息,我发现这是比较器写的不规范导致的。
Java的比较器有所谓的“一致性”原则,大概原理类似于:
Comparator异常:Comparison method violates its general contract!_第1张图片
因此,书写比较器时需要考虑到所有的情况。

上述的代码出现异常的原因,就是没有考虑lFile或rFile为null的情况。
尽管我能够保证这两个参数不为null,但JVM并不知道,它要求比较器的逻辑必须严谨,
严格考虑各种情况下两者大小的判定原则。

针对这个问题,可以按照下列代码进行修改:

public ArrayList getAllLogFiles(int flag) {
    ArrayList fileList = new ArrayList();

    //省略获取File的逻辑
    ........

    if (fileList.size() > 1) {
        ArrayList tmpList = new ArrayList(fileList);
        try {
            Collections.sort(tmpList, new Comparator() {
                @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 lTime = lFile.lastModified();
                    Long rTime = rFile.lastModified();
                    return lTime.compareTo(rTime) * (-1);
                }
            });
            fileList = tmpList;
            //lastModified may meet ErrnoException and return 0
            //which may cause "Comparison method violates its general contract!"
        } catch (IllegalArgumentException e) {
            .........
            //主动捕获了exception
            //主要是获取文件修改时间时,有可能导致ErrnoException,使得违反比较器的"一致性"原则
        }
    }

    return fileList;
}

你可能感兴趣的:(Android开发,Java零碎知识记录)