【プログラミングしてみよう47】JavaのComparableで自作クラスに自然順序を定義する

Java

前回はComparatorを使って外部からソート基準を定義する方法を学びました。

今回は「Comparable(コンパラブル)」について解説します。 Comparatorはクラスのいるからソートるのにするのに対し、Comparableはクラス自身に「自分の並び順」を定義します。Collections.sort() をそのまま使えるようにしたい自作クラスに Comparable を実装しましょう。

ComparableとComparatorの違い

前回学んだ Comparator は「外から比較基準を渡す」仕組みで、ソートするたびに比較方法を指定できます。Comparable は「クラス自身に比較方法を定義する」仕組みで、そのクラスの「デフォルトの並び順」を決めます。

// Comparable: クラス自身に定義
public class Product implements Comparable<Product> {
    public int compareTo(Product other) {
        return this.price - other.price; // 価格の昇順がデフォルト
    }
}

// Comparator: 外から指定
Comparator<Product> byName = (a, b) -> a.name.compareTo(b.name);

StringInteger などの標準クラスはすでに Comparable を実装しているため、Collections.sort() でそのまま並べ替えられます。自作クラスも Comparable を実装することで同じように使えるようになります。

サンプルコード(Comparableの実装)

ファイル作成

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

コード入力

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

public class Product implements Comparable<Product> {
    String name;
    int price;
    int stock;

    Product(String name, int price, int stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    // 価格の昇順を自然順序として定義
    @Override
    public int compareTo(Product other) {
        return this.price - other.price;
    }

    @Override
    public String toString() {
        return name + "(¥" + price + ", 在庫:" + stock + ")";
    }
}

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

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

public class ComparableTest {
    public static void main(String[] args) {
        List<Product> products = new ArrayList<>();
        products.add(new Product("コーヒー",   350, 100));
        products.add(new Product("お茶",       150,  50));
        products.add(new Product("ジュース",   200,  80));
        products.add(new Product("ミネラルウォーター", 100, 200));
        products.add(new Product("ラテ",       400,  30));

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

        // Collections.sort()で自然順序(価格昇順)でソート
        Collections.sort(products);
        System.out.println("\n--- 価格昇順(自然順序) ---");
        products.forEach(System.out::println);

        // 最小・最大も自然順序で判定される
        System.out.println("\n最安値: " + Collections.min(products));
        System.out.println("最高値: " + Collections.max(products));

        // TreeSetは自然順序で自動ソートして重複排除
        TreeSet<Product> productSet = new TreeSet<>(products);
        System.out.println("\n--- TreeSet(自動ソート) ---");
        productSet.forEach(System.out::println);
    }
}

コンパイル

javac Product.java ComparableTest.java

実行

java ComparableTest

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

--- 元の順番 ---
コーヒー(¥350, 在庫:100)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
ミネラルウォーター(¥100, 在庫:200)
ラテ(¥400, 在庫:30)

--- 価格昇順(自然順序) ---
ミネラルウォーター(¥100, 在庫:200)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
コーヒー(¥350, 在庫:100)
ラテ(¥400, 在庫:30)

最安値: ミネラルウォーター(¥100, 在庫:200)
最高値: ラテ(¥400, 在庫:30)

--- TreeSet(自動ソート) ---
ミネラルウォーター(¥100, 在庫:200)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
コーヒー(¥350, 在庫:100)
ラテ(¥400, 在庫:30)

サンプルコード(ComparableとComparatorの組み合わせ)

Comparable で自然順序を定義しつつ、Comparator で別の基準でもソートできます。

ファイル作成

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

コード入力

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

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

public class ComparablePlusTest {
    public static void main(String[] args) {
        List<Product> products = new ArrayList<>();
        products.add(new Product("コーヒー",   350, 100));
        products.add(new Product("お茶",       150,  50));
        products.add(new Product("ジュース",   200,  80));
        products.add(new Product("ミネラルウォーター", 100, 200));
        products.add(new Product("ラテ",       400,  30));

        // 自然順序(価格昇順)
        products.sort(Comparator.naturalOrder());
        System.out.println("--- 自然順序(価格昇順) ---");
        products.forEach(System.out::println);

        // 自然順序の逆(価格降順)
        products.sort(Comparator.reverseOrder());
        System.out.println("\n--- 逆順(価格降順) ---");
        products.forEach(System.out::println);

        // Comparatorで在庫数の昇順にソート
        products.sort(Comparator.comparingInt(p -> p.stock));
        System.out.println("\n--- 在庫数(昇順) ---");
        products.forEach(System.out::println);

        // 在庫数の昇順 → 同じ場合は自然順序(価格昇順)
        products.sort(Comparator.<Product>comparingInt(p -> p.stock)
                                .thenComparing(Comparator.naturalOrder()));
        System.out.println("\n--- 在庫数(昇順) → 価格(昇順) ---");
        products.forEach(System.out::println);
    }
}

コンパイル

javac ComparablePlusTest.java

実行

java ComparablePlusTest

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

--- 自然順序(価格昇順) ---
ミネラルウォーター(¥100, 在庫:200)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
コーヒー(¥350, 在庫:100)
ラテ(¥400, 在庫:30)

--- 逆順(価格降順) ---
ラテ(¥400, 在庫:30)
コーヒー(¥350, 在庫:100)
ジュース(¥200, 在庫:80)
お茶(¥150, 在庫:50)
ミネラルウォーター(¥100, 在庫:200)

--- 在庫数(昇順) ---
ラテ(¥400, 在庫:30)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
コーヒー(¥350, 在庫:100)
ミネラルウォーター(¥100, 在庫:200)

--- 在庫数(昇順) → 価格(昇順) ---
ラテ(¥400, 在庫:30)
お茶(¥150, 在庫:50)
ジュース(¥200, 在庫:80)
コーヒー(¥350, 在庫:100)
ミネラルウォーター(¥100, 在庫:200)

コードの解説

Comparable<Product> を実装した Product クラスでは、compareTo メソッドで this.price - other.price を返しています。結果が負なら this が前、正なら other が前、0なら同順です。価格の差を返すことで価格昇順がデフォルトの並び順になります。

Comparable を実装したことで Collections.sort() がそのまま使えるだけでなく、Collections.min()Collections.max() も自然順序(価格)で判定されるようになります。また TreeSet は挿入と同時に自動ソートするコレクションで、Comparable が実装されていれば自然順序で並べられます。

Comparator.naturalOrder()Comparable で定義した自然順序をComparatorとして使う書き方です。Comparator.reverseOrder() でその逆順になります。thenComparing(Comparator.naturalOrder()) のように、第2条件として自然順序を指定することもできます。

まとめ

Comparable を実装することで、自作クラスにデフォルトの並び順(自然順序)を定義できます。compareTo メソッドで比較結果を返し、Collections.sort()TreeSet などがその順序を自動的に使います。前回学んだ Comparator は外から基準を指定する柔軟な方法、Comparable はクラス自身に標準の順序を組み込む方法として使い分けましょう。両方を組み合わせることで、デフォルトは価格順・必要に応じて在庫数順といった柔軟な設計が実現できます。

2026/07/26(日) ぱんまる

コメント

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