こんにちは、かつコーチです。
自作クラスのListを並び替えたいとき、sortメソッドを呼んでも「どの基準で並べるか」が分からずエラーになった経験はありませんか。
今回はJavaで並び替えの基準を決めるComparableとComparatorの違いと使い分けを解説します。
ComparableとComparableの役割
なぜ並び替えの基準を教える必要があるのか
数値や文字列は「大きい・小さい」の基準が明確ですが、自作クラス(例:Userクラス)は何を基準に並べればいいかJavaには分かりません。
そこでJavaは「このクラスはこう並べてください」という基準を定義する仕組みを2つ用意しています。
ComparableとComparatorの違い
- Comparable(クラス自身に「標準の並び順」を持たせるインターフェース):クラス自体に
compareToメソッドを実装する - Comparator(外部から並び替えの基準を指定するインターフェース):クラスを変更せずに、その場で並び替えのルールを渡す
| 観点 | Comparable | Comparator |
|---|---|---|
| 実装場所 | 並べたいクラス自身に実装 | クラスの外に別途用意する |
| 基準の数 | 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 thenComparingやreversedを使うと複数条件・降順の並び替えも簡潔に書ける
次に読むべき記事
- Mapの基本:HashMap・TreeMap・LinkedHashMapの違い
- Iteratorの仕組みと拡張for文との関係
タグ: Java, 中級者向け, コレクション