Collections.sort()方法

时间:2015-07-22 07:32:41

标签: java comparator

我有字符串的比较器,它被转换为日期。当我将这个比较器传递给Collections.sort()方法时,我得到 java.lang.IllegalArgumentException:比较方法违反了它的一般合约!

我已经阅读了一些关于此例外的文章,但我并不清楚为什么会出现此异常。有什么想法吗?

private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");   

    Comparator<String> comparator = new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        if (o1 == null && o2 == null) {
                            return 0;
                        }
                        if (o1 == null) {
                            return 1;
                        }
                        if (o2 == null) {
                            return -1;
                        }
                        try {
                            Date first = sdf.parse(o1);
                            Date second = sdf.parse(o2);
                            return first.compareTo(second);
                        } catch (Exception ignored) {
                            return 0;
                        }
                    }
                };

5 个答案:

答案 0 :(得分:4)

如果抛出异常,则返回0.这意味着无论何时无法解析任何参数,两者都被视为相等。想想这个例子:

a = "01/01/2015"
b = "01/01/2016"
c = "xxx"

然后你得到

comparator.compare(a,c) = 0
comparator.compare(b,c) = 0

comparator.compare(a,b) != 0

解决方案:尝试单独解析每个字符串,并在异常时使用null,如下所示:

private SimpleDateFormat sdf = new SimpleDateFormat(“dd / MM / yyyy HH:mm”);

Comparator<String> comparator = new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        Date first;
        try {
            first = sdf.parse(o1);
        } catch (Exception ignored) {
            first = null;
        }
        Date second;
        try {
            second = sdf.parse(o2);
        } catch (Exception ignored) {
            second = null;
        }

        if (first == second) {
            return 0;
        }
        if (first == null) {
            return 1;
        }
        if (second == null) {
            return -1;
        }
        return first.compareTo(second);
    }
};

答案 1 :(得分:2)

问题出在您的try catch区块中。

即使其中一个日期不可解析,您也会返回0(这意味着对象相等)。

现在让我们采取这种情况。

str1 = "invalid";
str2 = "10/10/2015"; //Consider this to be valid format.
str3 = "12/10/2015";

现在,让我们来看看比较,

  1. 比较 str1 str2 :返回0(表示相等)
  2. 比较 str1 str3 :返回0(表示相等)
  3. 这意味着,当您比较 str2 str3 时,应该是equal。 (A=BA=C表示B=C)。

    但是当它比较时,它会返回一个负数。因此例外。

答案 2 :(得分:1)

尼斯。你的问题实际上并不那么难......想象一下,你有三个弦乐......

Date 1 = correct date string for "Today" Date 2 = correct date string for "Tomorrow" Date 3 = XYZ (an non-correct date String that will throw an exception when parsed)

Date 1 < Date 2, obviously. Date 2 > Date 1, also obvious, works fine

但现在是魔术/问题:

Date 3 == Date 1 - because of your exception handling

Date 3 == Date 2 - also because of that (for both you will return 0).

所以有一个与1和2都是EQUAL的Date,但是1和2不相等。

问问自己,你会在你的清单中将日期3放在哪里?它必须与日期1处于相同的“位置”(因为它与== 0比较)并且与日期2处于相同的“位置”(再次,它与== 0比较)。但是日期1和日期2不在同一位置。这是不可能的,因此你得到的“比较方法违反了它的一般合同!”。异常。

答案 3 :(得分:1)

您的字符串集可能有不可解决的日期。这导致在没有意义的情况下返回0。

String a = "badString";
String b = "20/12/2012 12:13";
String c = "20/12/2015 13:14";

b小于c且c大于b。因此,b和c不相等。但是你的函数说它们都等于String a!这没有任何意义,Collections.sort无法正确排序。

b需要在c之前,c需要在b之后,但是需要在b AND c旁边。

处理比较器的更好方法是首先过滤字符串列表,这样您才能比较有效日期。然后,您的函数可以抛出一个关于仍然无法解析日期的RuntimeException。

答案 4 :(得分:0)

java.util.Arrays.sort和(间接)java.util.Collections.sort使用的排序算法已被替换。如果新的排序实现检测到违反Comparable合同的Comparable,则可能抛出IllegalArgumentException。以前的实现默默地忽略了这种情况。如果需要以前的行为,则可以使用新的系统属性java.util.Arrays.useLegacyMergeSort来恢复先前的mergesort行为