Java Error: java.lang.IllegalArgumentException: Comparison method violates its general contract

Java Error: java.lang.IllegalArgumentException: Comparison method violates its general contract

我正在开发一个旧应用程序,它最初是用 Java 6 编写的,几年前升级到 Java 7。

在此应用程序中,我使用 Collection.Sort 通过实现 Comparator 接口,使用自定义 compare 方法对列表进行排序。列表中的对象类型为 CompanySchedule,具有 3 个属性 companyNameScheduleexpirationdate

列表可以包含多个具有相同 companyName 但具有唯一到期日期的对象。下面的比较函数按 companyName 的升序对列表进行排序,并在同一 companyName 列表中按截止日期的降序对列表进行排序。下面是方法实现。

    public int compare(CompanySchedule c1, CompanySchedule c2) {
        int returnVal = 0;
        int value = c1.getCompany().getName().compareTo(c2.getCompany().getName());
        if (value == 0){
            if (c1.getUseExpirationDate() == null || c2.getUseExpirationDate() == null){
                returnVal = -1;
            }
            else{
                int chkdate = c1.getUseExpirationDate().compareTo(c2.getUseExpirationDate());
                if (chkdate == 0){
                    returnVal = 0;
                }
                else if (chkdate > 0){
                    returnVal = -1;
                }
                else if (chkdate < 0){
                    returnVal = 1;
                }
            }
        }
        else if (value < 0){
            returnVal = -1;
        }
        else if (value > 0){
            returnVal = 1;
        }


        return returnVal;
    }

我知道在上面的比较方法实现中不满足传递属性时报错 java.lang.IllegalArgumentException: Comparison method violates its general contract 会被抛出。

有人可以帮助确定此方法将在何处违反传递 属性。 感谢您的帮助。

我认为这里有一个问题:

if (c1.getUseExpirationDate() == null || c2.getUseExpirationDate() == null){
                returnVal = -1;
}

如果 a.getUseExpirationDate() == null 并且 b.getUseExpirationDate() == null,您将得到 a < b 和 b < a,这意味着 a < a.

这破坏了一致性。 这个方法可能问题比较多,我没有全部检查过

祝你好运。

编辑

这段代码怎么样?

public int compare(CompanySchedule c1, CompanySchedule c2) {
        int returnVal = 0;
        int value = c1.getCompany().getName().compareTo(c2.getCompany().getName());
        if (value == 0) {
            if (c1.getUseExpirationDate() == null && c2.getUseExpirationDate() != null) {
                returnVal = -1;
            } else if (c1.getUseExpirationDate() != null && c2.getUseExpirationDate() == null) {
                returnVal = 1;
            } else if (c1.getUseExpirationDate() == null && c2.getUseExpirationDate() == null) {
                returnVal = 0;
            } else {
                int chkdate = c1.getUseExpirationDate().compareTo(c2.getUseExpirationDate());
                if (chkdate == 0) {
                    returnVal = 0;
                } else if (chkdate > 0) {
                    returnVal = -1;
                } else if (chkdate < 0) {
                    returnVal = 1;
                }
            }
        } else if (value < 0) {
            returnVal = -1;
        } else if (value > 0) {
            returnVal = 1;
        }


        return returnVal;
    }

为了可比性,我尽量不改动太多,但应该重构一下。基本上它确定空值小于其他值。

我知道我来晚了,一旦我找到了我基本上忘记了我问过这个问题的解决方案。所以正如我提到的,根据我对 Collections 内部实现的理解,应用程序是在 Java 6 中编写的,然后升级到 Java 7

Sort 方法将使用的算法从 Merge 排序更改为 Tim Sort,Merge Sort 忽略了比较方法实现的传递性 属性,但是,Tim 排序要求比较方法实现是传递性的,否则上述异常会被抛出,因为我们有一个遗留应用程序,我们不想从代码中更改任何东西,所以我们在排序实现中使用 jvm 参数 java.util.Arrays.useLegacyMergeSort=true 内部合并排序