2015-07-22 125 views
2

我有比較字符串,它被轉換爲日期。當我將這個比較器傳遞給Collections.sort()方法時,我得到了java.lang.IllegalArgumentException:比較方法違反了它的一般合約!IllegalArgumentException對Collections.sort()方法

我已經閱讀了關於這個異常的一些文章,但我不明白爲什麼會出現這個異常。任何想法 ?

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; 
         } 
        } 
       }; 
+0

您的字符串集合可能包含錯誤的格式字符串,您的示例數據是什麼列表? – STaefi

+0

請添加完整的stacktrace。 o1和o2的值是什麼? – Jens

+0

@Jens解析日期沒有問題,問題在於比較器的邏輯 –

回答

3

如果拋出異常,則返回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); 
    } 
}; 
+0

[只有'Objects.compare'方法](http://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#compare-TT-java.util.Comparator-)需要一個比較器,除此之外,它似乎並沒有提供一個「空」策略。但是,OP已經在他原來的代碼中處理了'null'的方式就足夠了。 – Holger

+0

@holger是您的第一個異議。但第二個不適用。這不是關於傳遞null,而是導致問題的異常處理。當我回家時會更新我的答案。目前的答案可能有些太不莊重。 – Axel

+0

也許你誤解了我。使用你的解決方案,不包括最後一行,接着是OP的代碼如何處理'null'(三個'if'語句),最後'返回first.compareTo(second);'將起作用。 – Holger

1

不錯。您的問題實際上並不難......想象一下,你有三根弦......

​​

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

但現在的魔術/問題:

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

AND

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

所以有一個等於1和2的日期,但1和2是不相等的。

問問自己,你會把日期3放在你的列表中?它必須與日期1(因爲它與== 0進行比較)和與日期2相同的「位置」處於相同的「位置」(同樣,它與== 0進行比較)。但日期1和日期2不在同一位置。這是不可能的,因此你正在得到你的「比較方法違反它的總合同!」。例外。

+0

我明白了,真的不難。謝謝 –

2

問題在於您的try catch區塊。

即使1個日期不可分析,您將返回0(表示對象相等)。

現在我們來看看這個條件。

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

現在,讓我們跑過來的比較,

  1. 比較STR1STR2:返回0(這意味着相等)
  2. 比較STR1STR3:返回0(意思相同)

這意味着,當您比較str2str3,那麼應該是equal。 (A=BA=C意味着B=C)。

但是當它比較時,它返回一個負數。因此例外。

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和c。

處理比較器的更好方法是先過濾字符串列表,以便僅比較有效日期。然後你的函數可以拋出一個RuntimeException,但仍然無法解析日期。

0

由java.util.Arrays.sort和java.util.Collections.sort(間接)使用的排序算法已被替換。如果新的排序實現檢測到違反可比較合同的Comparable,則可能會拋出IllegalArgumentException。以前的實現默默地忽略了這種情況。如果需要以前的行爲,則可以使用新的系統屬性java.util.Arrays.useLegacyMergeSort恢復先前的合併行爲

+0

你爲什麼只是默默地忽略你程序中的錯誤?這似乎不是一個好的解決方案。 – mhlz

相關問題