作業一覧を「未着手を先、同じ状態なら期限が近い順」に並べた。最初はよく見えたのに、期限が未設定のデータが来ると例外になり、同じ期限の行も表示のたびに順序が揺れる。
複数条件のソートは、比較式を一行へ詰めるより、利用者が期待する順序を表にしてから Comparator を組む方が安全です。以下は Java 21 の例です。
まず並び順を文章ではなく表にする
| 優先順位 | 比較する値 | 順序 |
|---|---|---|
| 1 | 状態 | 未着手 → 対応中 → 完了 |
| 2 | 期限 | 早い日付 → 遅い日付 → 未設定 |
| 3 | ID | 小さい順。完全同順位の決着用 |
「期限なし」は最後にします。ここを決めずに Comparator.comparing(Ticket::dueDate) と書くと、null の比較時に NullPointerException になり得ます。また、画面に同じ状態・期限のチケットが複数あるなら、IDまで比べて順序を固定します。
thenComparing で上から順に比較する

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
enum State { OPEN, IN_PROGRESS, DONE }
record Ticket(long id, State state, LocalDate dueDate) {}
class TicketOrder {
static int priority(State state) {
return switch (state) {
case OPEN -> 0;
case IN_PROGRESS -> 1;
case DONE -> 2;
};
}
static final Comparator<Ticket> ORDER =
Comparator.comparingInt((Ticket t) -> priority(t.state()))
.thenComparing(Ticket::dueDate,
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparingLong(Ticket::id);
public static void main(String[] args) {
List<Ticket> tickets = new ArrayList<>(List.of(
new Ticket(4, State.OPEN, null),
new Ticket(2, State.IN_PROGRESS, LocalDate.of(2026, 9, 24)),
new Ticket(3, State.OPEN, LocalDate.of(2026, 9, 23)),
new Ticket(1, State.OPEN, LocalDate.of(2026, 9, 23))));
tickets.sort(ORDER);
System.out.println(tickets.stream().map(Ticket::id).toList());
// [1, 3, 4, 2]
}
}thenComparing は前の比較が同じ場合だけ次の条件を使います。OPEN同士なら期限を見て、それも同じならIDを見る、という動きです。enumの宣言順をそのまま優先順位に使う手もありますが、表示順を業務仕様として明示するため、この例では switch にしました。
降順にする場所を間違えない
「期限が新しいものから。ただし未設定は最後」に変えるなら、期限の比較器だけを逆順にします。
.thenComparing(Ticket::dueDate,
Comparator.nullsLast(Comparator.reverseOrder()))比較器全体の最後へ .reversed() を付けると、状態とIDの順序まで逆になり、nullsLast の意図も崩れます。逆にしたい条件の内側へ逆順を置いてください。
List自体を並べ替えるか、新しいListを作るか
上の tickets.sort(ORDER) は元のListを書き換えます。List.of(...) から作ったListには変更操作を使えないため、例では new ArrayList<>(...) にしています。元データを残したいなら new ArrayList<>(original) を作ってからソートします。ただし要素オブジェクトまで複製されるわけではありません。
確認時は、状態が違う、状態だけ同じ、期限まで同じ、期限が null、全件同順位を含む入力を用意します。特にデータベースからページごとに取得する一覧では、Java側で取得後に並べ替えるだけではページ全体の順序を保証できません。DB問い合わせ側の ORDER BY に同じ条件を反映する必要があります。
