Java の一般契約エラーに違反する比較メソッドの修正
-
Comparator
インターフェイスとComparable
インターフェイスの違い -
Comparator
とComparable
の比較規則 -
比較メソッドを持つ Java コードは、その一般的な契約に違反しています
エラー -
Comparator
およびComparable
インターフェイスを使用した Java ソリューション
今日は、Comparator
および Comparable
インターフェースで使用される比較規則について学びます。これは、Java で 比較メソッドが一般的な契約に違反しています
エラーの考えられる原因につながります。 その後、Comparator
および Comparable
インターフェイスを使用した 2つのソリューションを理解します。
Comparator
インターフェイスと Comparable
インターフェイスの違い
これらは、Java コア ライブラリの 2つのインターフェイスです。 Comparator
は、2つの異なるオブジェクトを比較する関数であり、他のものから独立しています。
2つの入力のみを検索し、それらを使用してプロセスを続行し、結果を表示します。
一方、Comparable
は、データ クラスと混合するインターフェイスです。 たとえば、いくつかのデータを持つクラスがあります。 Comparable
インターフェイスは、このクラスの最初のオブジェクトを同じクラスの 2 番目のオブジェクトと比較するために使用されます。
これは、Comparable
インターフェースが、this
インスタンスを同じクラスの別のインスタンスと比較できることを示していることを意味します。 Comparable
は、クラスの 自然な
順序を定義することを思い出してください。
他の点では、これは Comparator
と同様の比較関数でもあり、戻り値、推移的、再帰的などについて同じ規則があります。
Comparator
と Comparable
の比較規則
comparison method violations its general contract
は、Comparator
または Comparable
(使用しているものに基づく) にバグがあり、整合性規則の 1つに違反していることを意味します。 整合性ルールとは何ですか?
以下でそれらを学びましょう。
整数型の値用の Java プログラムを作成したと仮定します。 したがって、比較関数は次の規則に従う必要があります。
-
任意の 2つの整数
a
とb
が与えられた場合、三分法を満たす必要があります。これは、次の関係のいずれかが真でなければならないことを意味します。a
はb
より小さい (a < b
の場合は-ve
値を返す)a
はb
に等しい (a == b
の場合は0
を返す)a
はb
より大きい (a > b
の場合は+ve
値を返す)
-
推移性を満たす必要があります。つまり、
a < b
およびb < c
の場合、任意の 3つの数値a
、b
、c
について、a < c
を意味します。 -
3 番目の規則は反対称性に関するもので、
a < b
は~b < a
を意味します。 -
代用可能性は、
a == b
およびa < c
と仮定する比較規則でもあります。 これはb < c
を意味します。 -
最後の比較規則は Reflexivity です。ここで、
a == a
;~a < a
.
これらのルールのいずれかに違反すると、比較方法が一般的な契約に違反しています
というエラーが表示されます。 以下のコード例の助けを借りてそれを学びましょう。
比較メソッドを持つ Java コードは、その一般的な契約に違反しています
エラー
コード例:
// import libraries
import static java.util.stream.Collectors.toCollection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
// Main
public class Main {
public static void main(String[] args) {
// generate random numbers
List<Integer> list = new Random(209).ints(32L).boxed().collect(toCollection(ArrayList::new));
// sort using lambda expression
list.sort(logging((a, b) -> a - b));
} // end main()
// logging the comparisons
static Comparator<Integer> logging(Comparator<Integer> c) {
return (a, b) -> {
int r = c.compare(a, b);
System.err.printf("%,14d %,14d => %,14d\n", a, b, r);
return r;
};
} // end logging()
} // end class
このコードでは、乱数のストリームを生成するために使用される Random
クラスの ints
インスタンスを使用して、いくつかの乱数を生成しています。
このコードでは、list.sort((a, b) -> a - b);
のように並べ替えると、 そうすると、何が問題で、どこで発生しているのかを特定できなくなります。 そのため、識別しやすくするために logging
で並べ替えています。
エラーが発生しますが、数値の比較も多数提供されます。 すべてを説明するわけではありませんが、そのうちのいくつかはエラーを見つけるのに十分です.
出力:
ご覧のとおり、プログラムはここで一貫性ルールに違反しており、このエラーが発生しています。 次のセクションでは、Comparator
と Comparable
を使用して解決策を考えてみましょう。
Comparator
および Comparable
インターフェイスを使用した Java ソリューション
乱数の少ない Comparator
インターフェイスを使用してみましょう。
コード例:
// import libraries
import static java.util.stream.Collectors.toCollection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
// Main
public class Main {
public static void main(String[] args) {
// generate random numbers
List<Integer> list = new Random(5).ints(32L).boxed().collect(toCollection(ArrayList::new));
// sort using lambda expression
list.sort(logging((a, b) -> a - b));
} // end main()
// logging the comparisons
static Comparator<Integer> logging(Comparator<Integer> c) {
return (a, b) -> {
int r = c.compare(a, b);
System.err.printf("%,14d %,14d => %,14d\n", a, b, r);
return r;
};
} // end logging()
} // end class
このコードは正常に実行されます。 すべての比較を行っているわけではありませんが、確認のためにいくつか見ていきます。
次のスクリーンショットを確認してください。
出力:
Comparable
インターフェイスを使用して比較を行い、エラーのない Java コードについて学びましょう。 このインターフェイスを使用するには、データを含むクラスを作成する必要があります。
以下にしましょう。
コード例 (Students.java
クラス):
public class Student implements Comparable<Student> {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int compareTo(Student other) {
/*
compare the last names and save the result
in `compareResult` variable
*/
int compareResult = this.lastName.compareTo(other.lastName);
/*
If both last names match, then it would be true means 0. So,
dive into the `if` condition and check if the count of their
first name matches.
if this.count == other.count return 0
if this.count > other.count return 1
if this.count < other.count return -1
*/
if (compareResult == 0) {
if (this.firstName.chars().count() == other.firstName.chars().count()) {
compareResult = 0;
return compareResult;
} else if (this.firstName.chars().count() > other.firstName.chars().count()) {
compareResult = 1;
return compareResult;
} else {
compareResult = -1;
return compareResult;
}
} else {
return compareResult;
}
}
}
コード例 (Main.java
クラス):
public class Main {
public static void main(String[] args) {
Student std1 = new Student("Mehvish", "Ashiq");
Student std2 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 1: " + std1.compareTo(std2));
Student std3 = new Student("Aftab", "Ashiq");
Student std4 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 2: " + std3.compareTo(std4));
Student std5 = new Student("Mehr-un-nissa", "Ashiq");
Student std6 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 3: " + std5.compareTo(std6));
}
}
出力:
Result of Comparison 1: 0
Result of Comparison 2: -1
Result of Comparison 3: 1
このコードでは、2つのオブジェクトの lastName
が同じ場合、firstName
の文字数を比較します。
ご覧のとおり、結果は一貫しています。 a==b
、a<b
、および a>b
の場合、それぞれ 0
、-1
、および 1
を返します。