【Java】JavaのComparator・Comparableの違いと使い方で並び替えをカスタマイズする

Java

こんにちは、かつコーチです。
自作クラスのListを並び替えたいとき、sortメソッドを呼んでも「どの基準で並べるか」が分からずエラーになった経験はありませんか。
今回はJavaで並び替えの基準を決めるComparableComparatorの違いと使い分けを解説します。

ComparableとComparableの役割

なぜ並び替えの基準を教える必要があるのか

数値や文字列は「大きい・小さい」の基準が明確ですが、自作クラス(例:Userクラス)は何を基準に並べればいいかJavaには分かりません。
そこでJavaは「このクラスはこう並べてください」という基準を定義する仕組みを2つ用意しています。

ComparableとComparatorの違い

  • Comparable(クラス自身に「標準の並び順」を持たせるインターフェース):クラス自体にcompareToメソッドを実装する
  • Comparator(外部から並び替えの基準を指定するインターフェース):クラスを変更せずに、その場で並び替えのルールを渡す
観点ComparableComparator
実装場所並べたいクラス自身に実装クラスの外に別途用意する
基準の数1クラスにつき1つ(標準の順序)複数の基準を自由に作れる
主なメソッドcompareTo(T o)compare(T o1, T o2)
使う場面そのクラス共通の自然な順序を決めたいとき状況に応じて並び替えの基準を変えたいとき

基本の書き方

Comparableの実装

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

class Member implements Comparable<Member> {
    String name;
    int age;

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

    @Override
    public int compareTo(Member other) {
        return Integer.compare(this.age, other.age); // 年齢の昇順
    }

    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}

public class ComparableSample {
    public static void main(String[] args) {
        List<Member> members = new ArrayList<>();
        members.add(new Member("かつコーチ", 35));
        members.add(new Member("Aさん", 22));
        members.add(new Member("Bさん", 28));

        Collections.sort(members);
        System.out.println(members); // [Aさん(22), Bさん(28), かつコーチ(35)]
    }
}

compareToは「自分が相手より小さければ負の数、大きければ正の数、同じなら0」を返すのがルールです。
Integer.compareのような標準メソッドを使うと、この計算を自分で書かずに済みます。

Comparatorの実装

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

List<Member> members = new ArrayList<>(List.of(
    new Member("かつコーチ", 35),
    new Member("Aさん", 22),
    new Member("Bさん", 28)
));

// 名前の文字数順に並び替える
members.sort(Comparator.comparing(m -> m.name.length()));
System.out.println(members);

// 年齢の降順に並び替える
members.sort(Comparator.comparingInt((Member m) -> m.age).reversed());
System.out.println(members);

ComparatorはComparator.comparing(...)のようにラムダ式で条件を渡せるため、クラス自体を変更せずに好きな基準で並び替えられます。

よくあるつまずきポイント・エラー対処

Comparableを実装せずにsortしてエラーになった

実際に私がハマった経験ですが、自作クラスのListをそのままCollections.sortに渡してエラーになったことがあります。

// ❌Before:Comparableを実装していないクラス
class Member {
    String name;
    int age;
    Member(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

List<Member> members = new ArrayList<>();
members.add(new Member("かつコーチ", 35));
members.add(new Member("Aさん", 22));

Collections.sort(members); // コンパイルエラー

このコードは error: no suitable method found for sort(List<Member>) というコンパイルエラーになります。
Collections.sortは「並べる基準がある」ことを前提にしたメソッドで、Comparableを実装していないクラスは基準を持たないため使えません。

// ✅After:Comparatorをその場で渡す(クラスを変更しなくてよい)
Collections.sort(members, Comparator.comparingInt(m -> m.age));
System.out.println(members);

「クラス自体を変更できない、または複数の並び順が必要」という場面では、Comparableを実装するのではなくComparatorをその場で渡す方が柔軟です。

応用・一歩先の使い方

複数条件での並び替え(thenComparing)

List<Member> members = new ArrayList<>(List.of(
    new Member("Bさん", 28),
    new Member("Aさん", 28),
    new Member("かつコーチ", 22)
));

members.sort(
    Comparator.comparingInt((Member m) -> m.age)
              .thenComparing(m -> m.name)
);
System.out.println(members);
// かつコーチ(22), Aさん(28), Bさん(28) ※年齢が同じ場合は名前順

thenComparingをつなげることで、「まず年齢順、同じ年齢なら名前順」という複数条件の並び替えを簡潔に書けます。

降順にしたいときの書き方

members.sort(Comparator.comparingInt((Member m) -> m.age).reversed());

reversed()を最後に付けるだけで昇順・降順を切り替えられるため、compareToの中身を反転させるより読みやすいコードになります。

まとめ

この記事のポイント

  • Comparableはクラス自身に標準の並び順(compareTo)を持たせる仕組み
  • Comparatorはクラスの外から並び替えの基準(compare)を指定する仕組み
  • 基準が1つで固定ならComparable、状況に応じて変えたいならComparator
  • thenComparingreversedを使うと複数条件・降順の並び替えも簡潔に書ける

次に読むべき記事

  • Mapの基本:HashMap・TreeMap・LinkedHashMapの違い
  • Iteratorの仕組みと拡張for文との関係

タグ: Java, 中級者向け, コレクション

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