当前位置: 首页 > 知识库问答 >
问题:

我的比较器方法如何违反了它的一般契约?

房时铭
2023-03-14
import java.util.Collections;
import java.util.Comparator;
import org.joda.time.DateTime;
public static Comparator<Task> TASK_PRIORITY = new Comparator<Task>() {
    public int compare(Task task1, Task task2) {
        if (task1 == null && task2 == null) return 0;
        if (task1 == null) return +1; //null last
        if (task2 == null) return -1; //null last

        // Only consider retries after a task is retried 5+ times
        if (task1.getRetries() >= 5 || task2.getRetries() >= 5) {
            // Primary sort: retry count (ascending)
            int retriesCompare = Integer.compare(task1.getRetries(), task2.getRetries());
            if (retriesCompare != 0) return retriesCompare;
        }

        // Secondary sort: creation time (ascending, null first)
        int creationCompare = compareTimeNullFirst(task1.getCreationTime(), task2.getCreationTime());
        if (creationCompare != 0) return creationCompare;

        // Tertiary sort: load time (ascending, null last)
        int loadCompare = compareTimeNullLast(task1.getLoadTime(), task2.getLoadTime());
        if (loadCompare != 0) return loadCompare;

        return 0;
    }
};

private static int compareTimeNullLast(DateTime time1, DateTime time2) {
    if (time1 == null && time2 == null) return 0;
    if (time1 == null) return +1;
    if (time2 == null) return -1;
    if (time1.isBefore(time2) return -1;
    if (time1.isAfter(time2)) return +1;
    return 0;
}

private static int compareTimeNullFirst(DateTime time1, DateTime time2) {
    if (time1 == null && time2 == null) return 0;
    if (time1 == null) return -1;
    if (time2 == null) return +1;
    if (time1.isBefore(time2) return -1;
    if (time1.isAfter(time2)) return +1;
    return 0;
}
//tasks is a List<Task>
Collections.sort(tasks, TASK_PRIORITY);

我有时会得到一个 IllegalArgumentException for Compare 方法违反其一般合同!我可以始终如一地抛出此异常,实时数据运行足够长的时间,但我不确定如何解决问题的实际原因。

我的比较仪有什么问题?(具体来说,我违反了合同的哪一部分?)如何在不掩盖异常的情况下修复它?

  • 我使用的是 Java 7,如果不进行重大重写就无法升级。
  • 我可以通过将java.util.Arrays.useLegacyMergeSort设置为true来掩盖异常,但这不是一个理想的解决方案。
  • 我尝试创建测试来随机生成数据并验证每个合同条件。我无法抛出异常。
  • 我尝试删除重试比较周围的条件,但最终仍然得到了异常。
  • 此行引发异常:Collections.sort(tasks, TASK_PRIORITY);

共有1个答案

松新
2023-03-14

让我们从头开始。我对您的代码的解读是,您的比较器的逻辑是合理的。(我本可以避免使用null任务DateTime值,但这与您的问题无关。)

另一个可能导致此异常的原因是compare方法给出的结果不一致,因为任务对象正在更改。事实上,看起来(至少)改变重试计数在语义上是有意义的。如果有另一个线程正在更改任务,则在当前线程进行排序时可能会影响排序的字段…,这可能会导致IllegalArgumentException

(比较契约的一部分是,在对集合进行排序时,成对排序不会改变。)

然后你会这样说:

我使用<code>ImmutableSet。copyOf在排序之前复制列表,我在java.util.concurrent.locks.ReadWriteLock中的读锁下执行此操作。

复制集合不会复制集合的元素。这是一个浅拷贝。因此,您最终会得到两个包含相同对象的集合。如果另一个线程改变了任何对象(例如通过增加重试次数),这可能会改变对象的顺序。

锁定确保您有一致的副本,但这不是问题所在。

解决办法是什么?我可以想到几个:

>

  • 您可以在复制和排序时锁定某些对象以阻止对集合和元素对象的所有更新。

    你可以深度复制收藏;i、 e.创建一个包含原始集合元素副本的新集合。

    您可以创建包含与排序相关的Task对象的字段快照的轻量级对象;例如

    public class Key implements Comparable<Key> {
        private int retries;
        private DateTime creation;
        private DateTime load;
        private Task task;
    
        public Key(Task task) {
            this.task = task;
            this.retries = task.getRetryCount();
            ...
        }
    
        public int compareTo(Key other) {
            // compare using retries, creation, load
        }
    }
    

    这具有潜在的优势,即您复制的信息更少,并且您可以从Key对象的排序集合转到原始的任务对象。

    请注意,所有这些备选方案都比您当前所做的慢。我认为没有办法避免这种情况。

  •  类似资料:
    • 我在整理名单 排序代码: FinalSentence类标头: compareTo()实现: 这是例外: 对于一个小列表(少于 50 个元素),它可以工作。对于一个大型列表(它也应该与那些一起使用),它会引发此异常。列表的实例类型是 ArrayList,这并不重要。 我不知道如何深入了解这一点。列表已满,元素类型相同(那里没有多态性),但是对于大型列表,我得到了这个奇怪的例外。 有什么想法吗? 谢谢

    • 问题内容: 您好,以下是我的比较器的比较方法。我不确定是什么问题。我在堆栈溢出时查找了其他类似标题的问题和答案,但不确定我的方法有什么问题,但我一直在获取java.lang.IllegalArgumentException:比较方法违反了它的一般约定! 任何帮助将不胜感激 添加我得到的异常 问题答案: 您的方法 不是可 传递的 。如果和,则必须等于。 现在考虑这种情况: 对于,和,假设方法返回以下

    • 使用自定义比较器执行< code > Collection.sort using >时,我得到一个< code > Java . lang . illegalargumentexception:Comparison方法违反了它的一般约定 我理解这是一个问题,因为该方法是不可传递的。在我的比较器中,调用了多个方法,我确定了违反此规则的代码段。然而,我无法修复它,也看不到它的问题。

    • 一切似乎都运行良好(几天),但我只遇到了一次问题,并且很难重现该问题。 “比较方法违反了其总合同!”被抛出,完全让我措手不及。我有以下几点: 我的染色体类别: 我有一个ArrayList,我使用了两个Collections。排序(MyList)和集合。排序(MyList,Collections.reverseOrder())。到目前为止,他们仍在正常工作。我在100次跑步中只遇到过一次错误。这个实

    • 偏离变量只是包含以下字段的对象的一个实例: 附言时间对象是来自 Joda-Time 库的 DateTime 实例,TransportType 是包含常量火车、海船、驳船和卡车的枚举。 编辑: 好的,所以我将比较器编辑为以下内容: 但这显然违反了一般契约。我如何让它按时间排序,然后根据它们的其他属性对那些具有相等时间的对象进行排序,只关心它们是否相等?希望这有意义… 编辑:解决方案 谢谢大家回答我的

    • 嗨,下面是我的比较器的比较方法。我不知道哪里出了问题。我查了关于堆栈溢出的其他类似标题的问题和答案,但不确定我的方法有什么问题,但我不断得到java.lang.IllegalArgument异常:比较方法违反了它的一般合同! 任何帮助将不胜感激 添加我得到的异常