与经济数据的比较逻辑错误 - 比较方法违反了其总契约

时间:2014-10-12 05:34:03

标签: java compare illegalargumentexception compareto

我知道我的compareTo方法有一些问题,但不知道在哪里..

这是我尝试排序的数据:

我正在查看许多.txt文件(每行大约20,000行),其中每行有一个单点数据。我正在提取一个会计年度(格式化为YYYYqX,其中X为财务季度为1-4)并将其存储为字符串。我还提取了行业代码(六位数整数)和价格指数(存储为双精度)。它存储在DataPoint对象中。

我希望输出为三列,一个是会计年度,一个是行业代码,另一个是价格指数。我想要格式化的数据,以便财政年度有序(1991q1,1991q2,...,1992q1等),行业代码的订购量至少达到最大值。因此,会计年度专栏将为每个行业代码提供许多1991q1条目,并在该季度提供价格指数。然后,当1991q1的所有行业代码用尽时,将列出1991q2的所有行业代码等。

为了实现这一点,我按如下方式构建了DataPoint compareTo方法:

public int compareTo(DataPoint p) {
    int fiscalResult = compareFiscal(p.getFiscalQuarter());
    if (fiscalResult > 0) {
        return fiscalResult;
    } else if (fiscalResult < 0) {
        return fiscalResult;
    } else {
        if (sectorCode > 0) {
            if (sectorCode > p.getSectorCode()) {
                return sectorCode - p.getSectorCode();
            }
            else if (sectorCode < p.getSectorCode()){
                return p.getSectorCode() - sectorCode;
            }
            else {
                return 0; // Should never happen
            }
        }
        else if (industryCode > 0) {
            if (industryCode > p.getIndustryCode()) {
                return industryCode - p.getIndustryCode();
            }
            else if (industryCode < p.getIndustryCode()) {
                return p.getIndustryCode() - industryCode;
            }
            else {
                return 0; // Should never happen
            }
        }
        // These should never be reached
        else if (p.getSectorCode() > 0) {
            return -1;
        }
        else if (p.getIndustryCode() > 0) {
            return -1;
        }
        else {
            return 0;
        }
    }
}

其中compareFiscal(String)方法只是:

public int compareFiscal(String otherFiscal) {
    return fiscalQuarter.compareTo(otherFiscal);
}

fiscalQuarter是包含YYYYqX会计年度的String变量的名称。

当我之前说过行业代码时,实际上会有一个扇区代码(一个四位整数)或一个行业代码(六位整数)。 DataPoint不会同时具有这两者(它没有初始化为0),因此在compareTo方法中检查sectorCode或industryCode的值。

我可以在一个文件中对这些点的列表进行排序而没有问题,但是在我的程序结束时,我从每个文件中获取所有数据点并将它们放入一个新的ArrayList(两个列表,一个用于扇区代码和一个用于行业代码。在任何时候都不能将扇区和行业代码排序在一起),并在此列表中调用Collections.sort。这是引发错误的一点。

以下是我试图调用Collections.sort方法的一点(对于行业列表,相同的一个用于扇区列表)。 DataList只是另一个表示一个文件的对象,包含两个列表,一个是所有扇区DataPoints,另一个是所有行业DataPoints。 DataLists列表只包含从每个文件创建的所有DataLists。我不认为它能说明任何事情,但只是为了相关性:

public static List<DataPoint> formatIndustryData(List<DataList> dataLists) {
    List<DataPoint> data = new ArrayList<>();
    for (DataList list : dataLists) {
        data.addAll(list.getIndustryPoints());
    }
    Collections.sort(data);
    return data;
}

任何人都可以在compareTo方法中看到我的逻辑出错吗?

编辑:我忘了提到,如果财政年度不同,我们会得到一个与另一个相等的行业/行业代码。 (例如,在同一财政年度,相同的行业代码从来没有两个价格指数,因为这没有多大意义。)

此外,具有行业价值的DataPoints与具有扇区值的DataPoints相比毫不逊色 - 它们存储在单独的列表中,并且仅相互比较和排序。

1 个答案:

答案 0 :(得分:1)

你的逻辑似乎不完整。

如果this实例的sectorCode&gt; 0,你通过sectorCode进行比较,但是你没有处理p.sectorCode&lt; = 0的情况。

同样,如果this实例的行业代码&gt; 0,您通过industryCode进行比较,但是您没有处理p.industryCode&lt; = 0的情况。

您应该决定两个属性中的哪一个 - sectorCode和industryCode - 优先。

假设对象A具有sectorCode 5和industryCode 0。 对象B具有sectorCode 0和industryCode 6。

A.compareTo(B)返回1
B.compareTo(A)也返回1

这违反了compareTo的合约,因为A&gt; B和B&gt; A不能同时为真。

如果您想先通过sectorCode进行比较,那么您的代码应如下所示:

    if (sectorCode > 0) {
        if (sectorCode > p.getSectorCode()) {
            return 1;
        }
        else {
            return -1;
        }
    } else if (p.getSectorCode() > 0) {
        return -1;
    } else if (industryCode > 0) {
        if (industryCode > p.getIndustryCode()) {
            return 1;
        }
        else {
            return -1;
        }
    } else if (p.getIndustryCode() > 0) {
        return -1;
    } else {
        return 0; // Should never happen
    }

此外,如果industryCode == p.getIndustryCode()(两者都是正数)或sectorCode == p.getSectorCode()(两者都是正数),您应该返回0。