【プログラミングしてみよう46】JavaのComparatorでソートをカスタマイズする

Java

前回はIteratorとIterableの仕組みを理解し、自作クラスに拡張for文対応を実装しました。 今回は「Comparator(コンパレータ)」について解説します。 Collections.sort()List.sort() でリストをソートしてきましたが、自分でソートの基準を定義することで、複雑な並び替えも自由に実現できるようになります。

Comparatorとは

Collections.sort() はデフォルトで「自然順序」(数値なら昇順、文字列なら辞書順)でソートします。しかし「年齢の降順」「名前の長さ順」「複数の条件を組み合わせた順」など、独自のソート基準が必要な場合があります。

Comparator<T> は2つの要素を比較して順序を決めるためのインターフェースです。

public interface Comparator<T> {
    int compare(T o1, T o2);
}

compare メソッドは以下のルールで整数を返します。

負の値 → o1がo2より前(小さい)
0     → o1とo2は同じ順序
正の値 → o1がo2より後(大きい)

ラムダ式を使うと Comparator を1行で書けます。

サンプルコード(基本的なComparatorの使い方)

ファイル作成

「Java34」フォルダに ComparatorTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

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

public class ComparatorTest {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("ぱんまる");
        names.add("たろう");
        names.add("はなこ");
        names.add("あいこ");
        names.add("けんじ");

        // デフォルトソート(辞書順)
        Collections.sort(names);
        System.out.println("辞書順: " + names);

        // 文字数の少ない順
        names.sort(Comparator.comparingInt(String::length));
        System.out.println("文字数(昇順): " + names);

        // 文字数の多い順(reversed)
        names.sort(Comparator.comparingInt(String::length).reversed());
        System.out.println("文字数(降順): " + names);

        // 数値リストを降順にソート
        List<Integer> numbers = new ArrayList<>(List.of(5, 2, 8, 1, 9, 3));
        numbers.sort(Comparator.reverseOrder());
        System.out.println("数値(降順): " + numbers);

        // ラムダ式で独自比較
        List<String> fruits = new ArrayList<>(List.of("banana", "apple", "cherry", "kiwi"));
        fruits.sort((a, b) -> a.length() - b.length()); // 文字数で昇順
        System.out.println("果物(文字数順): " + fruits);
    }
}

コンパイル

javac ComparatorTest.java

実行

java ComparatorTest

実行結果は次のようになります。

辞書順: [あいこ, たろう, はなこ, ぱんまる, けんじ]
文字数(昇順): [たろう, はなこ, あいこ, けんじ, ぱんまる]
文字数(降順): [ぱんまる, たろう, はなこ, あいこ, けんじ]
数値(降順): [9, 8, 5, 3, 2, 1]
果物(文字数順): [kiwi, apple, banana, cherry]

サンプルコード(複数条件でのソート)

自作クラスのオブジェクトを複数条件でソートする例を作ります。

ファイル作成

「Java34」フォルダに Student.javaComparatorAdvTest.java という名前でファイルを作成します

コード入力

Student.java に以下のコードを入力します。

public class Student {
    String name;
    int score;
    int age;

    Student(String name, int score, int age) {
        this.name = name;
        this.score = score;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + "(点数:" + score + ", 年齢:" + age + ")";
    }
}

ComparatorAdvTest.java に以下のコードを入力します。

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

public class ComparatorAdvTest {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("ぱんまる", 85, 20));
        students.add(new Student("たろう",   90, 22));
        students.add(new Student("はなこ",   85, 19));
        students.add(new Student("けんじ",   72, 21));
        students.add(new Student("あいこ",   90, 20));

        System.out.println("--- 元の順番 ---");
        students.forEach(System.out::println);

        // 点数の降順
        students.sort(Comparator.comparingInt((Student s) -> s.score).reversed());
        System.out.println("\n--- 点数(降順) ---");
        students.forEach(System.out::println);

        // 点数の降順 → 同点なら年齢の昇順
        students.sort(
            Comparator.comparingInt((Student s) -> s.score).reversed()
                      .thenComparingInt(s -> s.age)
        );
        System.out.println("\n--- 点数(降順) → 年齢(昇順) ---");
        students.forEach(System.out::println);

        // 点数の降順 → 同点なら名前の辞書順
        students.sort(
            Comparator.<Student, Integer>comparing(s -> s.score).reversed()
                      .thenComparing(s -> s.name)
        );
        System.out.println("\n--- 点数(降順) → 名前(辞書順) ---");
        students.forEach(System.out::println);
    }
}

コンパイル

javac Student.java ComparatorAdvTest.java

実行

java ComparatorAdvTest

実行結果は次のようになります。

--- 元の順番 ---
ぱんまる(点数:85, 年齢:20)
たろう(点数:90, 年齢:22)
はなこ(点数:85, 年齢:19)
けんじ(点数:72, 年齢:21)
あいこ(点数:90, 年齢:20)

--- 点数(降順) ---
たろう(点数:90, 年齢:22)
あいこ(点数:90, 年齢:20)
ぱんまる(点数:85, 年齢:20)
はなこ(点数:85, 年齢:19)
けんじ(点数:72, 年齢:21)

--- 点数(降順) → 年齢(昇順) ---
あいこ(点数:90, 年齢:20)
たろう(点数:90, 年齢:22)
はなこ(点数:85, 年齢:19)
ぱんまる(点数:85, 年齢:20)
けんじ(点数:72, 年齢:21)

--- 点数(降順) → 名前(辞書順) ---
あいこ(点数:90, 年齢:20)
たろう(点数:90, 年齢:22)
ぱんまる(点数:85, 年齢:20)
はなこ(点数:85, 年齢:19)
けんじ(点数:72, 年齢:21)

コードの解説

Comparator.comparingInt(String::length) は文字列の長さを比較基準にするComparatorをシンプルに作ります。reversed() で逆順に、Comparator.reverseOrder() で自然順の逆順にできます。ラムダ式 (a, b) -> a.length() - b.length() のように直接比較ロジックを書くこともできます。

thenComparingInt()thenComparing() は「最初の比較条件が同じだった場合に適用する第2・第3の条件」を追加するメソッドです。メソッドチェーンでつなげることで、複数の優先順位を持つソートを簡潔に表現できます。

students.forEach(System.out::println)System.out::println は「メソッド参照」と呼ばれる書き方で、ラムダ式 s -> System.out.println(s) と同じ意味です。以前学んだラムダ式のさらに短い表現です。

まとめ

Comparator を使うことで、デフォルトのソート順以外の独自の並び替え基準を定義できます。Comparator.comparingInt()reversed() でシンプルに基準を作り、thenComparing() で複数条件を組み合わせるという流れを覚えておきましょう。ラムダ式やメソッド参照との組み合わせで、コードをすっきりと書けます。成績順・日付順・複合条件など、実際のアプリ開発でソートが必要な場面はとても多いので、ぜひ活用してみてください。

2026/07/25(土) ぱんまる

コメント

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