【问题标题】:Error : Comparison method violates its general contract错误:比较方法违反其总合同
【发布时间】:2017-04-30 23:11:53
【问题描述】:

我知道有很多与此主题相关的问题,但我无法完全理解导致此错误的原因

 Collections.sort(noteList,new Comparator<ClassNote>(){
        @Override
        public int compare(ClassNote b, ClassNote a) {
           DateFormat formatter = new SimpleDateFormat("MMMM dd HH:mm", Locale.US);
           try {
               Date date2 = formatter.parse(b.getCallDate());
               Date date1 = formatter.parse(a.getCallDate());
               if ( date1 == null ) {
                   if ( date2 == null) {
                       return 0;
                   }
                   return 1;
               }
               if ( date2 == null ) {
                   return -1;
               }
               return date2.compareTo(date1);
           } catch (ParseException e) {
               e.printStackTrace();
               return 1;
           }
        }
    });

有谁知道为什么它不起作用以及如何解决它?

【问题讨论】:

  • 你收到ParseException了吗?你检查过logcat吗?我不知道printStackTrace 在 Android 上做了什么。如果你得到任何解析异常,那么你的比较函数肯定是错误的,但首先我需要知道你是否有任何异常。如果没有,我看不出比较函数有什么问题。
  • 显然我得到了 ParseExceptions,它似乎搞砸了整个排序算法

标签: java android comparator


【解决方案1】:

我认为在这种情况下最好的计划是,不要专注于“我如何使我的比较函数保持一致”,而是问“我希望排序数组是什么样子”?当您处理特殊情况时,这一点尤其重要,就像您一样。找出正确的顺序后,编写一个比较函数来给出该顺序。

在这里,您正在处理三个类别的ClassNote

  1. ClassNote 具有有效日期的对象
  2. ClassNote 解析日期返回的对象 null
  3. ClassNote 解析日期引发异常的对象

类别 1 很简单:您希望对象按日期排序。 (您还必须弄清楚当两个 ClassNotes 具有相同日期时会发生什么,但在这种情况下,您可能不关心订单,这很好。在某些情况下,您可能希望具有相同日期的项目按其他字段排序。)

对于 #2 和 #3,您希望它们在结果中的哪个位置?到目前为止,您似乎首先需要null,但您还没有处理解析异常。我可以看到两种简单的方法:

  1. null 视为例外。然后所有的空值和异常情况都会以某种顺序出现并混合在一起,但这可能没问题。
  2. 对它们进行分组,使#2 项目在前,然后是#3,然后是具有有效日期的项目。或者其他方式:#3,然后是 #2,然后是有效日期。

任何一种方法都可以,但你必须决定你想要什么。

假设您采用方法#1。现在您必须安排一些事情,以便当您将#2 项目与#3 项目进行比较时,结果为 0;当您将#2 或#3 项目与#1 进行比较时,#2 或#3 总是更少。您可以修改现有代码

 Date date2 = formatter.parse(b.getCallDate());
 Date date1 = formatter.parse(a.getCallDate());

到这里:

 Date date2 = parseOrReturnNull(b.getCallDate());
 Date date1 = parseOrReturnNull(a.getCallDate());

其中parseOrReturnNull 是一个辅助方法,它使用formatter.parse(),捕获ParseException,如果出现异常则返回null。现在,您的其余逻辑将确保空值和解析异常将以相同的方式处理以用于排序目的。

如果您想分别对null 和异常进行分组,一种解决方案是添加一个“类别”变量,用于每个ClassNote。类别将按照正确的顺序指示ClassNote 所在的类别:因此,如果您首先需要nulls,然后是例外,然后是有效日期,您可以将类别设为0 表示null,1 表示例外, 2 为有效日期。 (更好的是,使用enum)。然后,对于每个日期,您将计算类别和date。事实上,最好声明一个新类:

class ComparisonDate {
    int category;  // or enum
    Date date;     // meaningful only if the date is valid
}

使用接受ClassNote 并返回ComparisonDate 的辅助方法。然后你可以用这样的代码比较两个ComparisonDates:

if (cd1.category != cd2.category) {
    return Integer.compare(cd1.category, cd2.category);
} else if (cd1.category == 2) {    // 2 means they have valid dates
    return cd1.date.compareTo(cd2.date);
} else {        // ComparisonDates are in same category and dates aren't
                // meaningful
    return 0;   // so treat them as equal
}

我可能已经切换了 cd1cd2,但你可以解决这个问题。

【讨论】:

  • 好答案,我实际上得到了一些 parseExceptions,现在一切都在你的答案的帮助下工作:)
【解决方案2】:

如果比较ClassNoteab的两个实例,解析其中一个会抛出异常:

compare(a, b) 将返回 a 更大。

compare(b, a) 将返回 b 更大。

比较返回值不一致。

【讨论】:

  • 你知道如何让比较返回值一致吗?
  • 我猜你应该尝试分别解析和捕获date1date2,然后相应地返回一个值(不像现在你总是返回1,不管哪个失败了解析)。
  • 也许指的是这个案例,因为它是空的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
相关资源
最近更新 更多