JavaのComparableとComparatorは、オブジェクトのコレクションをソートするのに非常に便利です。Javaには、プリミティブ型の配列やラッパークラスの配列、リストをソートするためのいくつかの組み込みメソッドが用意されています。まず、プリミティブ型とラッパークラスの配列/リストをソートする方法を学び、その後、java.lang.Comparableとjava.util.Comparatorインターフェースを使用して、カスタムクラスの配列/リストをソートします。単純なプログラムを使用して、プリミティブ型またはオブジェクトの配列やリストをソートする方法を見てみましょう。
package com.journaldev.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaObjectSorting {
/**
* This class shows how to sort primitive arrays,
* Wrapper classes Object Arrays
* @param args
*/
public static void main(String[] args) {
//int配列のようにプリミティブ型の配列をソートする
int[] intArr = {5,9,1,10};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr));
//String配列をソートする
String[] strArr = {"A", "C", "B", "Z", "E"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr));
//Wrapperクラスのオブジェクトのリストをソートする
List strList = new ArrayList();
strList.add("A");
strList.add("C");
strList.add("B");
strList.add("Z");
strList.add("E");
Collections.sort(strList);
for(String str: strList) System.out.print(" "+str);
}
}
上記のプログラムの出力は次のとおりです:
[1, 5, 9, 10]
[A, B, C, E, Z]
A B C E Z
次に、オブジェクトの配列をソートしてみましょう。
package com.journaldev.sort;
public class Employee {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
//このメソッドは、Employeeに関するユーザーフレンドリーな情報を表示するためにオーバーライドされます。
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
以下は、Employeeオブジェクトの配列をソートするために使用したコードです。
//オブジェクトの配列をソートする
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//Comparableインターフェースの実装を使用してemployees配列をソートする
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
これを実行しようとすると、次の実行時例外が発生します。
Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)
ComparableとComparator
Javaは、Comparableインターフェースを提供しており、ArraysやCollectionsのソートメソッドを使用する場合には、任意のカスタムクラスでこれを実装する必要があります。Comparableインターフェースには、ソートメソッドによって使用されるcompareTo(T obj)メソッドがあります。このメソッドは、Wrapperクラス、Stringクラス、またはDateクラスなどを確認することができます。このメソッドをオーバーライドする必要があります。オーバーライドする際には、このメソッドが「this」オブジェクトが引数として渡されたオブジェクトより小さい、等しい、または大きい場合にそれぞれ負の整数、ゼロ、または正の整数を返すようにします。EmployeeクラスでComparable インターフェースを実装した後、以下は結果となるEmployeeクラスです。
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//従業員をIDの昇順でソートしてみましょう
//この従業員のIDが指定されたオブジェクトより小さい、等しい、または大きい場合に
//それぞれ負の整数、ゼロ、または正の整数を返す必要があります。
return (this.id - emp.id);
}
@Override
//これは、Employeeに関するユーザーフレンドリーな情報を表示するために必要です。
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
以上のスニペットを実行して従業員の配列をソートし、結果を出力すると、以下のような出力が得られます。
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
従業員の配列がidで昇順に並べ替えられていることがわかります。しかし、ほとんどの実際のシナリオでは、異なるパラメータに基づいてソートしたい場合があります。例えば、CEOとして、私は給与に基づいて従業員をソートしたいと思いますし、人事部の場合は年齢に基づいてソートしたいと思うでしょう。このような状況では、Java Comparatorインターフェースを使用する必要があります。なぜなら、Comparable.compareTo(Object o)メソッドの実装はデフォルトのソートを提供し、動的に変更することはできないからです。一方、Comparatorを使用すると、異なるソート方法を持つ複数のメソッドを定義し、要件に応じてソート方法を選択することができます。
Java Comparator
Comparatorインターフェースのcompare(Object o1, Object o2)メソッドは、2つのオブジェクト引数を受け取るように実装する必要があります。このメソッドは、最初の引数が2番目の引数よりも小さい場合に負の整数を返し、等しい場合には0を返し、最初の引数が2番目の引数よりも大きい場合には正の整数を返します。ComparableおよびComparatorインターフェースは、コンパイル時の型チェックにジェネリックスを使用しています。詳細はJava Genericsを参照してください。ここでは、Employeeクラス内で異なるComparatorの実装方法を示します。
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
すべての上記のComparatorインターフェースの実装は無名クラスです。これらの比較子を使用して、ArraysとCollectionsクラスのsort関数に引数を渡すことができます。
//給与によるComparatorを使用して従業員配列を並べ替え
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//年齢によるComparatorを使用して従業員配列を並べ替え
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//名前によるComparatorを使用して従業員配列を並べ替え
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
以下は、上記のコードスニペットの出力です:
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Javaオブジェクト配列またはリストを並べ替えたい場合は、デフォルトの並べ替えを提供するためにjava Comparableインターフェースを実装する必要があり、異なる並べ替え方法を提供するためにJava Comparatorインターフェースを実装する必要があることがわかりました。また、Comparator
インターフェースを実装する別のクラスを作成して使用することもできます。これがJavaで ComparableとComparator について説明している最終のクラスです。
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//IDの昇順で従業員を並べ替えましょう
//この従業員IDで返される負の整数、ゼロ、または正の整数
//指定されたオブジェクトよりも小さい、等しい、または大きい
return (this.id - emp.id);
}
@Override
//これは、従業員についてのユーザーフレンドリーな情報を印刷するために必要です。
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator SalaryComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator AgeComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator NameComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
}
以下は、2つのEmployeesオブジェクトを最初にIDで比較し、同じ場合は名前で比較するComparatorインターフェースの別のクラス実装です。
package com.journaldev.sort;
import java.util.Comparator;
public class EmployeeComparatorByIdAndName implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
int flag = o1.getId() - o2.getId();
if(flag==0) flag = o1.getName().compareTo(o2.getName());
return flag;
}
}
これは、ComparableとComparatorを使用してJavaでオブジェクトを異なる方法で並べ替えるテストクラスです。
package com.journaldev.sort;
import java.util.Arrays;
public class JavaObjectSorting {
/**
* This class shows how to sort custom objects array/list
* implementing Comparable and Comparator interfaces
* @param args
*/
public static void main(String[] args) {
//オブジェクト配列のソート
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//Comparableインターフェースの実装による従業員の配列のソート
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
//Comparatorを使用して給与で従業員の配列をソート
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//Comparatorを使用して年齢で従業員の配列をソート
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//Comparatorを使用して名前で従業員の配列をソート
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
//Comparatorクラスを使用してIDと名前で従業員リストをソート
empArr[0] = new Employee(1, "Mikey", 25, 10000);
Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
}
}
上記のプログラムの出力は次のとおりです。
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]
java.lang.Comparableおよびjava.util.Comparatorは、Javaでオブジェクトをソートするために使用できる強力なインターフェースです。
Comparable vs Comparator
- Comparableインターフェースは単一のソート方法を提供するために使用されますが、Comparatorインターフェースは異なるソート方法を提供するために使用されます。
- Comparableを使用するには、クラスがそれを実装する必要がありますが、Comparatorを使用するにはクラスに変更を加える必要はありません。
- Comparableインターフェースは
java.lang
パッケージにありますが、Comparatorインターフェースはjava.util
パッケージにあります。 - Comparableを使用する場合、クライアント側でのコード変更は必要ありません。
Arrays.sort()
やCollection.sort()
メソッドは、自動的にクラスのcompareTo()
メソッドを使用します。Comparatorを使用する場合、クライアントはcompare()メソッドで使用するComparatorクラスを指定する必要があります。
Collections.sort()メソッドは、Comparator引数を受け取ることでStrategyパターンに従っています。
私たちのGitHubリポジトリから完全なコードとより多くのコアJavaの例をチェックアウトすることができます。
Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example