开发者社区> 问答> 正文

我的比较器方法

第三方引用

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对Comparison method violates its general contract!。我可以始终运行足够长的实时数据来抛出此异常,但是我不确定如何解决问题的实际原因。

我的问题 我的比较器怎么了?(具体来说,我违反了合同的哪一部分?)如何在不掩盖例外的情况下进行修复?

笔记

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

问题来源:Stack Overflow

展开
收起
montos 2020-03-27 10:11:27 384 0
1 条回答
写回答
取消 提交回答
  • 让我们从头开始。我阅读的代码是比较器的逻辑是正确的。(本来可以避免使用null Task和DateTimevalues,但这与您的问题无关。)

    导致此异常的另一件事compare是,由于Task对象在变化,该方法给出的结果不一致。确实,对于重试次数更改(至少),它看起来在语义上有意义。如果还有一个线程正在更改Task可能影响排序的字段...而当前线程正在排序...则可能会导致排序IllegalArgumentException。

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

    然后,您这样说:

    我ImmutableSet.copyOf通常在排序之前先复制列表,然后在中的读取锁定下进行复制java.util.concurrent.locks.ReadWriteLock。

    复制集合不会复制该集合的元素。它是浅表副本。因此,您最终将获得两个包含相同对象的集合。如果另一个线程更改了任何对象(例如,通过增加重试计数),则可能会更改对象的顺序。

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

    解决办法是什么?我可以想到几个: 1. 在复制和排序时,您可以锁定某些内容以阻止对集合和元素对象的所有更新。

    1. 您可以复制收集的内容;即创建一个包含原始集合元素副本的新集合。

    2. 您可以创建轻量级对象,其中包含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对象的排序集合到原始Task对象。

    请注意,所有这些替代方法都比您目前正在做的慢。我认为没有办法避免这种情况。

    回答来源:Stack Overflow

    2020-03-27 10:12:51
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载