Java Language
比較可能なコンパレータ
サーチ…
構文
- パブリッククラスMyClassは、Comparable
<MyClass
> - パブリッククラスMyComparatorは、Comparator
<SomeOtherClass
> - public int compareTo(MyClassその他)
- public int compare(SomeOtherClass o1、SomeOtherClass o2)
備考
double
に依存するcompareTo(..)
メソッドを実装する場合は、次の操作は行わないでください。
public int comareTo(MyClass other) {
return (int)(doubleField - other.doubleField); //THIS IS BAD
}
(int)
キャストによる切り捨てにより、メソッドが正または負の数ではなく0
を誤って返すことがあり、その結果、比較およびソートのバグにつながる可能性があります。
代わりに、 Double.compareを次のように使用するのが最も簡単な正しい実装です。
public int comareTo(MyClass other) {
return Double.compare(doubleField,other.doubleField); //THIS IS GOOD
}
Comparable<T>
ジェネリックでないバージョン、単にComparable
はJava 1.2以来存在しています 。レガシーコードとのインタフェース以外にも、ジェネリック版のComparable<T>
、比較時にキャストする必要がないため、実装する方が常に優れています。
クラスがそれ自身に匹敵することは、次のように非常に標準的です。
public class A implements Comparable<A>
このパラダイムから壊れる可能性はありますが、そうするときは慎重にしてください。
Comparator<T>
は、そのクラスがComparable<T>
実装している場合でも、そのクラスのインスタンスで使用できます。この場合、 Comparator
のロジックが使用されます。 Comparable
実装で指定された自然順序付けは無視されます。
Comparableを使ってリストをソートするまたはコンパレータ
私たちはPersonを名字で表すクラスを作っているとしましょう。これを行うための基本クラスを作成し、適切なequals
hashCode
とhashCode
メソッドを実装しました。
public class Person {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName){
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
}
次のシナリオのように、 Person
オブジェクトのリストを名前でソートするとします。
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //This currently won't work.
}
残念ながら、マークされているように、上記は現在コンパイルされません。 Collections.sort(..)
は、リスト内の要素が匹敵している場合、または比較のカスタムメソッドが指定されている場合にのみ、リストのソート方法を知っています。
あなたが次のリストを並べ替えるように求められたなら: 1,3,5,4,2
、答えは1,2,3,4,5
と言っても問題ありません。これは、整数(Javaと数学的には両方とも)が自然順序付けを持つためです。これは、標準のデフォルト比較ベース順序です。 Personクラスに自然順序付けを与えるために、 compareTo(Person p):
メソッドの実装が必要Comparable<Person>
を実装しcompareTo(Person p):
public class Person implements Comparable<Person> {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName) {
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public int compareTo(Person other) {
// If this' lastName and other's lastName are not comparably equivalent,
// Compare this to other by comparing their last names.
// Otherwise, compare this to other by comparing their first names
int lastNameCompare = lastName.compareTo(other.lastName);
if (lastNameCompare != 0) {
return lastNameCompare;
} else {
return firstName.compareTo(other.firstName);
}
}
}
さて、与えられた主なメソッドは正しく機能します
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Now functions correctly
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
ただし、クラスPerson
を変更したくない場合や変更できない場合は、任意の2つのPerson
オブジェクトの比較を処理するカスタムComparator<T>
を用意することができます。 circle, square, rectangle, triangle, hexagon
リストをソートするよう依頼された場合、コーナーの数に基づいてそのリストをソートするように求められた場合は、そうすることができます。ちょうど、比較器を提供することは、Javaに、2つの通常比較できないオブジェクトを比較する方法を指示します。
public class PersonComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
// If p1's lastName and p2's lastName are not comparably equivalent,
// Compare p1 to p2 by comparing their last names.
// Otherwise, compare p1 to p2 by comparing their first names
if (p1.getLastName().compareTo(p2.getLastName()) != 0) {
return p1.getLastName().compareTo(p2.getLastName());
} else {
return p1.getFirstName().compareTo(p2.getFirstName());
}
}
}
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
Comparatorは、匿名の内部クラスとして作成/使用することもできます
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
//Anonymous Class
Collections.sort(people, new Comparator<Person>() { //Legal
public int compare(Person p1, Person p2) {
//Method code...
}
});
}
ラムダ式コンパレータ
Java 8の時点では、コンパレータはラムダ式
//Lambda
Collections.sort(people, (p1, p2) -> { //Legal
//Method code....
});
コンパレータのデフォルトメソッド
さらに、Comparatorを構築するためのComparatorインターフェイスには、興味深いデフォルトメソッドがあります。次は、 lastName
とfirstName
比較器を作成しfirstName
。
Collections.sort(people, Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName));
コンパレータの順序を反転する
任意の比較も容易に使用して逆にすることができるreversedMethod
降順に昇順に変更されます。
compareToメソッドとcompareメソッド
Comparable<T>
インターフェイスには、1つのメソッドが必要です。
public interface Comparable<T> {
public int compareTo(T other);
}
また、 Comparator<T>
インターフェイスには、1つのメソッドが必要です。
public interface Comparator<T> {
public int compare(T t1, T t2);
}
:これらの2つの方法は、1つのマイナー違いが、基本的に同じことを行うcompareTo
比較しthis
するother
のに対し、 compare
比較t1
するt2
ではなく、思いやりのすべてについてthis
。
その違いとは別に、2つの方法にも同様の要件があります。特に(compareToの場合)、 このオブジェクトを指定されたオブジェクトと比較して注文します。このオブジェクトが指定されたオブジェクトよりも小さい、等しい、または大きい場合は、負の整数、ゼロまたは正の整数を返します。したがって、 a
とb
比較のa
:
-
a < b
、a.compareTo(b)
およびcompare(a,b)
が負の整数を返し、b.compareTo(a)
およびcompare(b,a)
が正の整数を返す必要がある場合 -
a > b
、a.compareTo(b)
およびcompare(a,b)
が正の整数を返し、b.compareTo(a)
およびcompare(b,a)
が負の整数を返す必要がある場合 - 場合等しい
a
b
比較のために、すべての比較は返す必要があります0
。
自然(比較可能)と明示(比較)ソート
2つのCollections.sort()
メソッドがあります:
- かかる一
List<T>
パラメータとしてT
匹敵実装し、上書きしなければならないcompareTo()
ソート順序を決定する方法。 - 引数としてListとComparatorをとり、Comparatorがソート順を決定するものです。
まず、Comparableを実装するPersonクラスを次に示します。
public class Person implements Comparable<Person> {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.getAge() - o.getAge();
}
@Override
public String toString() {
return this.getAge()+"-"+this.getName();
}
}
上記のクラスを使用して、 compareTo()
メソッドのオーバーライドによって定義された要素の自然順序付けでリストをソートする方法を次に示します。
//-- usage
List<Person> pList = new ArrayList<Person>();
Person p = new Person();
p.setName("A");
p.setAge(10);
pList.add(p);
p = new Person();
p.setName("Z");
p.setAge(20);
pList.add(p);
p = new Person();
p.setName("D");
p.setAge(30);
pList.add(p);
//-- natural sorting i.e comes with object implementation, by age
Collections.sort(pList);
System.out.println(pList);
匿名のインラインコンパレータを使用して、Comparableを実装していないListをソートするか、この場合、自然順序付け以外の順序でListをソートする方法は次のとおりです。
//-- explicit sorting, define sort on another property here goes with name
Collections.sort(pList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(pList);
マップエントリのソート
Java 8以降では、マップ反復のソートを可能にするためにMap.Entry
インターフェースにデフォルトのメソッドがありMap.Entry
。
Map<String, Integer> numberOfEmployees = new HashMap<>();
numberOfEmployees.put("executives", 10);
numberOfEmployees.put("human ressources", 32);
numberOfEmployees.put("accounting", 12);
numberOfEmployees.put("IT", 100);
// Output the smallest departement in terms of number of employees
numberOfEmployees.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(1)
.forEach(System.out::println); // outputs : executives=10
もちろん、これらはストリームAPIの外側でも使用できます。
List<Map.Entry<String, Integer>> entries = new ArrayList<>(numberOfEmployees.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());
比較メソッドを使用したコンパレータの作成
Comparator.comparing(Person::getName)
これは、このPerson
名を比較元として使用するPerson
クラスのコンパレーターを作成します。メソッドバージョンを使用してlong、int、doubleを比較することもできます。例えば:
Comparator.comparingInt(Person::getAge)
逆順
逆順を使用するコンパレータを作成するには、reverse reversed()
メソッドを使用します。
Comparator.comparing(Person::getName).reversed()
コンパレータのチェーン
Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)
これは、firsが姓と比較してファーストネームと比較するコンパレータを作成します。多くのコンパレータを必要に応じてチェーンすることができます。