サードパーティのパッケージ
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
までtrue
、それは望ましい解決策ではありません。 - 私は、ランダムデータを生成し、それぞれを検証するためのテストを作成しようとした契約条件を。私は、例外がスローされるように取得することができませんでした。
- 私は、再試行の比較を中心に条件を削除しようとしたが、私はまだ最終的に例外を得ました。
- この行は、例外がスローされます。
Collections.sort(tasks, TASK_PRIORITY);
私たちが最初に始めましょう。あなたのコードの私の読書はあなたのコンパレータのロジックが健全であるということです。(私はnullを持つ避けているだろうTask
とDateTime
の値を、それはあなたの問題には関係ありません。)
場合は、この例外が発生することができ、他の事があるcompare
ので、この方法は矛盾する結果を与えているTask
オブジェクトが変更されています。それがために意味的に意味があるように確かに、それは変更に(少なくとも)の再試行回数を探します。変化している他のスレッドがある場合はTask
、現在のスレッドがソートされている間...発注を行うことができるフィールドを...それができましたIllegalArgumentException
。
(比較契約の一部は、あなたがコレクションを並べ替えている間にそのペアごとの順序は変更されませんです。)
その後、これを言います:
私が使用して
ImmutableSet.copyOf
ソートする前にリストをコピーするために、そして私がで読み取りロックの下でそれを行いますjava.util.concurrent.locks.ReadWriteLock
。
コレクションをコピーすると、コレクションの要素のコピーを作成しません。これは浅いコピーです。だから、同じオブジェクトを含む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
のオブジェクト。
これらの選択肢のすべては、あなたが現在何をしているかよりも遅いです注意してください。私はこれを回避する方法があるとは思いません。