Comparator インターフェース
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 45/87。
Comparable はクラスの自然な順序を定義しますが、時にはオブジェクトを別の方法でソートする必要がある場合があります。Comparator インターフェースを使用すると、元のクラスを変更せずに、別の比較ロジックを作成できます。
Comparator は、同じ型の2つのオブジェクトを比較する外部オブジェクトです。これは、複数のソートオプションが必要な場合や、クラス自体を修正できない場合に役立ちます:
import java.util.Comparator;
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}その後、コンパレータをソートメソッドに渡すことができます:
List<Person> people = new ArrayList<>();
people.add(new Person("Charlie", 30));
people.add(new Person("Alice", 25));
Collections.sort(people, new NameComparator()); // 名前でソート
Collections.sort(people, new AgeComparator()); // 年齢でソートComparatorは関数型インターフェースであるため、ラムダ式を使用して簡潔なコードを書くことができます:
Collections.sort(people, (p1, p2) -> p1.getName().compareTo(p2.getName()));主な違い:Comparable は比較対象のクラス自体によって実装され、1つの自然な順序を定義します。一方、Comparator は無制限のカスタム順序を定義できる別のクラスです。
チャレンジ
簡単Comparator インターフェースの強力さを示す従業員ソートシステムを構築しましょう。Employee クラス自体を変更することなく、給与順や名前順など、さまざまな方法で従業員をソートするための複数のコンパレータを作成します。
コードは以下の4つのファイルに分けて構成します:
Employee.java: 3つのプライベートフィールドname(String)、department(String)、salary(double) を持つ従業員を表すクラスを作成します。すべてのフィールドを初期化するコンストラクタと、それぞれのゲッターメソッドを含めてください。toString()をオーバーライドして、[name] ([department]) - $[salary]を返すようにします。SalaryComparator.java: 従業員を給与の降順(給与の高い順)でソートするコンパレータを作成します。Comparator<Employee>インターフェースとそのcompare()メソッドを実装してください。給与値の安全な比較にはDouble.compare()を使用します。NameComparator.java: 従業員を名前のアルファベット順で昇順(AからZ)にソートする別のコンパレータを作成します。Comparator<Employee>を実装し、比較には String のcompareTo()メソッドを使用します。Main.java: 従業員のリストを作成し、作成したコンパレータを使用してソートすることで、すべてをまとめます。3人の従業員の入力を、各行name,department,salaryの形式で受け取ります。従業員の
ArrayListを作成し、両方のソート方法を実演してください:- まず、
SalaryComparatorを使用して給与順にソートし、各従業員を出力します。 - 空行を1行出力します。
- 次に、
NameComparatorを使用して名前順にソートし、各従業員を出力します。
- まず、
入力として、従業員データが name,department,salary の形式で含まれる3行を受け取ります。
例えば、入力行は Alice,Engineering,75000.0 のようになります。
コンパレータファイルでは java.util.Comparator を、Main ファイルでは java.util.ArrayList、java.util.Collections、java.util.Scanner をインポートすることを忘れないでください。コンパレータを使用することで、外部から複数のソート戦略を定義できることに注目してください。Employee クラスは、自分がどのようにソートされるかについて何も知る必要はありません。
チートシート
Comparatorインターフェースを使用すると、元のクラスを変更せずにオブジェクトをソートするための外部比較ロジックを作成できます。これは、複数のソートオプションが必要な場合に便利です。
Comparatorは、compare()メソッドを実装することで、同じ型の2つのオブジェクトを比較します。
import java.util.Comparator;
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}コンパレータをソートメソッドに渡します。
Collections.sort(people, new NameComparator());Comparatorは関数型インターフェースであるため、ラムダ式を使用して簡潔なコードを記述できます。
Collections.sort(people, (p1, p2) -> p1.getName().compareTo(p2.getName()));数値を比較する場合は、安全な比較のためにDouble.compare()のようなラッパークラスのメソッドを使用します。
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}主な違い: Comparableはクラス自体の中で1つの自然な順序を定義しますが、Comparatorは外部で無制限のカスタム順序を定義できる別のクラスです。
自分で試してみよう
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力から3人の従業員を読み込む
// 各行の形式: 名前,部署,給与
String line1 = scanner.nextLine();
String line2 = scanner.nextLine();
String line3 = scanner.nextLine();
// TODO: 各行を解析し、Employeeオブジェクトを作成する
// ヒント: 値を分割するには split(",") を使用する
// TODO: 従業員のArrayListを作成し、3人全員を追加する
// TODO: SalaryComparatorを使用して給与順にソートし、各従業員を出力する
// ヒント: Collections.sort(list, comparator) を使用する
// TODO: 空行を出力する
// TODO: NameComparatorを使用して名前順にソートし、各従業員を出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造7特殊メソッドとObjectクラス
toString() メソッドequals() と hashCode()clone() メソッドcompareTo() と ComparableComparator インターフェースまとめ - カスタムソート