第三方引用
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!。我可以始终运行足够长的实时数据来抛出此异常,但是我不确定如何解决问题的实际原因。
我的问题 我的比较器怎么了?(具体来说,我违反了合同的哪一部分?)如何在不掩盖例外的情况下进行修复?
笔记
问题来源:Stack Overflow
让我们从头开始。我阅读的代码是比较器的逻辑是正确的。(本来可以避免使用null Task和DateTimevalues,但这与您的问题无关。)
导致此异常的另一件事compare是,由于Task对象在变化,该方法给出的结果不一致。确实,对于重试次数更改(至少),它看起来在语义上有意义。如果还有一个线程正在更改Task可能影响排序的字段...而当前线程正在排序...则可能会导致排序IllegalArgumentException。
(比较合同的一部分是,在对集合进行排序时,成对排序不会改变。)
然后,您这样说:
我ImmutableSet.copyOf通常在排序之前先复制列表,然后在中的读取锁定下进行复制java.util.concurrent.locks.ReadWriteLock。
复制集合不会复制该集合的元素。它是浅表副本。因此,您最终将获得两个包含相同对象的集合。如果另一个线程更改了任何对象(例如,通过增加重试计数),则可能会更改对象的顺序。
锁定可确保您具有一致的副本,但这不是问题所在。
解决办法是什么?我可以想到几个: 1. 在复制和排序时,您可以锁定某些内容以阻止对集合和元素对象的所有更新。
您可以复制收集的内容;即创建一个包含原始集合元素副本的新集合。
您可以创建轻量级对象,其中包含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
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。