Java Tips | コレクション:ソート

Java Java
スポンサーリンク

コレクションのソートは「並び順というビジネスルール」をコードにする作業

ソートは単なる「小さい順・大きい順」ではありません。
業務では「名前の五十音順」「日付の新しい順」「ステータス順(独自の優先度)」など、
ビジネスルールそのものが「並び順」として現れます。

だからこそ、ソートは「アルゴリズムの話」よりも
「どんな順番にしたいのかを、分かりやすくコードに書く」ことが重要になります。


基本の List ソートを押さえる

Collections.sort と List.sort の違い

まずは一番基本の「昇順ソート」です。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SortBasic {

    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(3);
        numbers.add(1);
        numbers.add(2);

        Collections.sort(numbers); // 昇順に並び替え

        System.out.println(numbers); // [1, 2, 3]
    }
}
Java

Java 8 以降なら、List#sort もよく使います。

numbers.sort(Integer::compareTo);
Java

どちらも「元の List をその場で並び替える」点が重要です。
つまり、ソートは“破壊的操作”です。

業務では「元の順番も必要」「ソート後の順番も必要」ということがよくあるので、
その場合は先にコピーを作ってからソートするのが安全です。

List<Integer> sorted = new ArrayList<>(numbers);
Collections.sort(sorted);
Java

ここでの大事な感覚は、
「ソートは元のデータを変える操作なので、コピーするかどうかを意識する」ことです。


Comparator で「並び順のルール」に名前をつける

文字列を昇順・降順に並べる

Comparator を使うと、「どう並べるか」を柔軟に指定できます。

import java.util.Comparator;
import java.util.List;

public class SortString {

    public static void main(String[] args) {
        List<String> names = new java.util.ArrayList<>(
                List.of("山田", "佐藤", "鈴木")
        );

        // 昇順(自然順)
        names.sort(Comparator.naturalOrder());
        System.out.println(names); // [佐藤, 山田, 鈴木] など

        // 降順
        names.sort(Comparator.reverseOrder());
        System.out.println(names); // [鈴木, 山田, 佐藤] など
    }
}
Java

ここでのポイントは、「Comparator は“並び順のルール”そのもの」ということです。
業務でよく出てくる「独自の順番」も、Comparator に閉じ込めると読みやすくなります。

オブジェクトを特定の項目でソートする

例えば、ユーザーを「年齢の昇順」で並べたいとします。

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.Comparator;
import java.util.List;

public class SortUser {

    public static void main(String[] args) {
        List<User> users = new java.util.ArrayList<>(
                List.of(
                        new User("山田", 30),
                        new User("佐藤", 25),
                        new User("鈴木", 40)
                )
        );

        users.sort(Comparator.comparingInt(User::getAge));

        for (User u : users) {
            System.out.println(u.getName() + " " + u.getAge());
        }
        // 佐藤 25
        // 山田 30
        // 鈴木 40
    }
}
Java

ここでの重要ポイントは、「Comparator.comparingInt(User::getAge)」という一行が
「年齢の昇順で並べる」というビジネスルールをそのまま表していることです。


複数条件ソートで「業務っぽい順番」を表現する

まず年齢昇順、同じなら名前昇順

業務では「まずこれで並べて、同じならこれで…」という複数条件ソートがよく出てきます。
Comparator はそれをとても素直に書けます。

users.sort(
        Comparator.comparingInt(User::getAge)
                  .thenComparing(User::getName)
);
Java

この一行で、
「年齢の昇順、年齢が同じなら名前の昇順」というルールを表現できます。

コードを読む人は、if 文を追いかけなくても、
「どういう順番なのか」を一目で理解できます。

ここが、Comparator を“業務ルールの言語”として使ううえで、非常に大事なポイントです。


null を含むデータをソートするときの注意点

null を先頭・末尾に寄せる Comparator

現実のデータには、よく null が混ざります。
そのままソートすると NullPointerException になることもあります。

Comparator には、null を安全に扱うためのヘルパーがあります。

import java.util.Comparator;

Comparator<String> byNameNullsLast =
        Comparator.nullsLast(Comparator.naturalOrder());

Comparator<String> byNameNullsFirst =
        Comparator.nullsFirst(Comparator.naturalOrder());
Java

使い方のイメージです。

List<String> names = new java.util.ArrayList<>(
        List.of("山田", null, "佐藤")
);

names.sort(Comparator.nullsLast(Comparator.naturalOrder()));
System.out.println(names); // [佐藤, 山田, null] など
Java

ここでの重要ポイントは、「null をどう扱うかも“並び順のルール”の一部」だということです。
「null は最後に寄せる」「null は先頭に寄せる」「そもそも null は許さない」
どれを選ぶかを、Comparator で明示しておくと、後から読んだ人にも意図が伝わります。


不変リストとソートの組み合わせ方

「ソート済みの不変リスト」をユーティリティで作る

List.of(...)List.copyOf(...) で作った不変リストは、そのままではソートできません。
業務では「ソート済みの不変リスト」が欲しいことも多いので、
ユーティリティでまとめてしまうと便利です。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public final class SortedLists {

    private SortedLists() {}

    public static <T> List<T> sorted(List<T> source, Comparator<? super T> comparator) {
        if (source == null || source.isEmpty()) {
            return List.of();
        }
        List<T> copy = new ArrayList<>(source);
        copy.sort(comparator);
        return List.copyOf(copy); // ソート済み不変リスト
    }
}
Java

使い方はこうです。

List<String> raw = List.of("B", "A", "C");

List<String> sorted =
        SortedLists.sorted(raw, Comparator.naturalOrder());

System.out.println(sorted); // [A, B, C]
// sorted.add("X"); // UnsupportedOperationException
Java

ここでの重要ポイントは、「ソートと不変化をセットで扱っている」ことです。
「このリストはこの順番で固定」という意図を、
型と実装で表現できます。


まとめ:ソートユーティリティで身につけてほしい感覚

ソートは、単に「小さい順に並べるテクニック」ではなく、
「ビジネス上の並び順を、Comparator という形でコードに刻む」 作業です。

元の List を壊すかどうかを意識し、必要ならコピーしてからソートする。
Comparator を使って、「何で並べるか」「昇順か降順か」「同値のときどうするか」を明示する。
複数条件ソートや null の扱いも、Comparator でルールとして書き下す。
必要なら「ソート済み不変リスト」を返すユーティリティを用意し、「この順番で固定」という意図を表現する。

あなたが書くソートコードが、
「アルゴリズム」ではなく「業務ルールの宣言」に見えるようになってきたら、
それはもう立派に“実務で使えるソートユーティリティ”を扱えている状態です。

タイトルとURLをコピーしました