逆順ソートは「優先度の高いものを先頭に持ってくる」ための技
ソートというと「小さい順・古い順」をイメージしがちですが、
業務ではむしろ「新しい順」「優先度の高い順」「金額の大きい順」が圧倒的に多いです。
つまり、逆順ソートは“ビジネス的に重要なものを先頭に持ってくるための技”です。
ここをきちんとユーティリティ化しておくと、
「毎回 Comparator をひねり出す」苦労から解放されます。
基本:数値や文字列を逆順ソートする
Collections.reverseOrder / Comparator.reverseOrder を使う
まずは一番シンプルな「自然順の逆」を押さえましょう。
数値や文字列など、「普通に並べる順番」が決まっている型は、reverseOrder を使うだけで逆順にできます。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ReverseSortBasic {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(5);
numbers.add(20);
// 昇順
Collections.sort(numbers);
System.out.println(numbers); // [5, 10, 20]
// 逆順(大きい順)
numbers.sort(Comparator.reverseOrder());
System.out.println(numbers); // [20, 10, 5]
}
}
Javaここで重要なのは、
「Comparator.reverseOrder() は“自然順の完全な逆”を作ってくれる」
という感覚です。
文字列も同じです。
List<String> names = new ArrayList<>(List.of("山田", "佐藤", "鈴木"));
names.sort(Comparator.reverseOrder());
System.out.println(names); // 自然順の逆(辞書順の逆)
Java「とりあえず逆順にしたい」なら、Comparator.reverseOrder() を覚えておけば十分戦えます。
オブジェクトを「特定の項目の逆順」でソートする
comparingInt(…).reversed() という“定番パターン”
業務では、「オブジェクトの特定のフィールドを基準に逆順ソート」がほぼ定番です。
例えば、「年齢の高い順に並べたい」ユーザークラスを考えます。
class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
Javaこれを「年齢の高い順」に並べるコードはこう書けます。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ReverseSortUser {
public static void main(String[] args) {
List<User> users = new ArrayList<>(
List.of(
new User("山田", 30),
new User("佐藤", 25),
new User("鈴木", 40)
)
);
users.sort(
Comparator.comparingInt(User::getAge)
.reversed()
);
for (User u : users) {
System.out.println(u.getName() + " " + u.getAge());
}
// 鈴木 40
// 山田 30
// 佐藤 25
}
}
Javaここで深掘りしたいポイントは二つです。
一つ目は、Comparator.comparingInt(User::getAge) が
「年齢の昇順」というルールを表し、.reversed() がそれを「年齢の降順」にひっくり返している、という構造です。
二つ目は、「“何で並べるか”を先に書いてから、最後に .reversed() を付ける」
という書き方にすると、“仕様としての読みやすさ”が格段に上がることです。
複数条件の逆順ソートをきれいに書く
「まず日付の新しい順、同じ日付ならIDの昇順」
業務では、「まずこれの逆順、同じならこれの昇順」という複合条件がよく出ます。
例えば、注文を「注文日の新しい順、同じ日付なら注文IDの昇順」で並べたいとします。
import java.time.LocalDate;
class Order {
private final String id;
private final LocalDate orderedAt;
public Order(String id, LocalDate orderedAt) {
this.id = id;
this.orderedAt = orderedAt;
}
public String getId() { return id; }
public LocalDate getOrderedAt() { return orderedAt; }
}
JavaComparator で書くとこうなります。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ReverseSortOrder {
public static void main(String[] args) {
List<Order> orders = new ArrayList<>(
List.of(
new Order("A003", LocalDate.of(2024, 1, 10)),
new Order("A001", LocalDate.of(2024, 1, 12)),
new Order("A002", LocalDate.of(2024, 1, 12))
)
);
orders.sort(
Comparator.comparing(Order::getOrderedAt)
.reversed() // 日付の新しい順
.thenComparing(Order::getId) // 同じ日付ならID昇順
);
}
}
Javaここでの重要ポイントは、
「昇順の Comparator を組み立ててから、必要なところだけ .reversed() を付ける」
というスタイルです。
これにより、
「まず注文日で並べる(逆順)→同じならIDで並べる(昇順)」
という仕様が、そのままコードとして読めるようになります。
逆順ソートと「コピーしてからソート」のユーティリティ化
「元データはそのまま、逆順ソート済みのリストが欲しい」
実務では、「元のリストは触らず、逆順に並んだ結果だけ欲しい」ことが多いです。
そのたびにコピー+ソートを書くのは面倒なので、ユーティリティにしてしまいましょう。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public final class ReverseSortedLists {
private ReverseSortedLists() {}
public static <T> List<T> reversed(List<T> source, Comparator<? super T> comparator) {
if (source == null || source.isEmpty()) {
return List.of();
}
List<T> copy = new ArrayList<>(source);
copy.sort(comparator.reversed());
return List.copyOf(copy); // 逆順ソート済み不変List
}
}
Java使い方のイメージです。
List<Integer> numbers = List.of(10, 5, 20);
List<Integer> desc =
ReverseSortedLists.reversed(numbers, Comparator.naturalOrder());
System.out.println(desc); // [20, 10, 5]
// desc.add(1); // UnsupportedOperationException
Javaここでの重要ポイントは、
「逆順ソートと“不変化”をセットで扱っている」ことです。
「この結果は“この順番で固定”」という意図を、
戻り値の性質で保証できます。
null を含むデータの逆順ソート
null を最後に寄せる逆順ソート
現実のデータには null が混ざるので、
逆順ソートでも「null をどこに置くか」を決めておく必要があります。
import java.util.Comparator;
public final class NullSafeReverseSorts {
private NullSafeReverseSorts() {}
public static final Comparator<Integer> INT_DESC_NULLS_LAST =
Comparator.nullsLast(Comparator.<Integer>naturalOrder().reversed());
}
Java使い方のイメージです。
List<Integer> nums = new java.util.ArrayList<>(
List.of(10, null, 5, 20)
);
nums.sort(NullSafeReverseSorts.INT_DESC_NULLS_LAST);
// [20, 10, 5, null] のような並び
Javaここでのポイントは、
「nullsLast と .reversed() の組み合わせ順を意識する」ことです。
Comparator.nullsLast(昇順).reversed() とComparator.nullsLast(昇順.reversed()) は挙動が変わるので、
「null を最後にしたいのか、先頭にしたいのか」を意識して書き分ける必要があります。
まとめ:逆順ソートユーティリティで意識してほしいこと
逆順ソートは、
「ビジネス的に“重要なものを先頭に持ってくる”ための標準テクニック」です。
自然順の逆なら Comparator.reverseOrder() を素直に使う。
オブジェクトなら Comparator.comparing(...).reversed() で「何を基準に逆順か」を明示する。
複数条件では「どこを逆順にするか」を Comparator チェーンで表現する。
元データを壊したくないときは「コピーして逆順ソート+不変化」をユーティリティにしておく。
null を含む場合は nullsFirst / nullsLast と .reversed() の組み合わせを意識して、「null の位置」も仕様として決める。
あなたの逆順ソートのコードが、
「なんとなく大きい順」ではなく
「ビジネス的な優先度順」として読めるようになってきたら、
それはもう立派に“業務で使える逆順ソートユーティリティ”を扱えている状態です。

