How to Fix Comparison Method Violates Its General Contract Error in Java
-
Difference Between
Comparator
andComparable
Interfaces -
Comparison Rules for
Comparator
andComparable
-
Java Code Having the
comparison method violates its general contract
Error -
Java Solutions Using the
Comparator
andComparable
Interfaces
Today, we will learn about the comparison rules used by the Comparator
and Comparable
interfaces, which will lead to possible causes of the comparison method violates its general contract
error in Java. After that, we will understand two solutions using Comparator
and Comparable
interfaces.
Difference Between Comparator
and Comparable
Interfaces
These are two interfaces in Java core libraries. The Comparator
is a function that compares two different objects and is independent of anything else.
It only looks for its two inputs, continues its process with them, and presents the results.
On the other hand, the Comparable
is an interface that we mix with a data class. For instance, we have a class with some data; the Comparable
interface will be used to compare this class’s first object with the same class’s second object.
It means the Comparable
interface indicates that this
instance can be compared with another instance of the same class. Remember, the Comparable
defines the natural
order for the class.
In other respects, it is also a comparison function just like the Comparator
and has the same rules for the return value, transitive, reflexive, etc.
Comparison Rules for Comparator
and Comparable
The comparison method violates its general contract
means that the Comparator
or Comparable
(based on what we are using) has a bug and violates one of the consistency rules. What are the consistency rules?
Let’s learn them below.
We assume that we have written our Java program for integer-type values. So, our comparison function must adhere to the following rules.
-
Given any two integers
a
andb
, the Trichotomy Law must be satisfied, which means exactly one of the following relations must be true:a
is less thanb
(it returns-ve
value ifa < b
)a
equalsb
(it returns0
ifa == b
)a
is greater thanb
(it returns+ve
value ifa > b
)
-
It must satisfy the Transitivity, which means if
a < b
andb < c
then, for any three numbersa
,b
,c
, it impliesa < c
. -
The third rule is about Antisymmetry, where
a < b
implies~b < a
-
Substitutability is also a comparison rule that says, suppose,
a == b
anda < c
; this impliesb < c
. -
The final comparison rule is Reflexivity, where
a == a
; also~a < a
.
If any of these rules are violated, we get the error saying the comparison method violates its general contract
. Let’s learn it with the help of a code example below.
Java Code Having the comparison method violates its general contract
Error
Example Code:
// 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
In this code, we are generating a few random numbers by using the ints
instance of the Random
class, which is used to generate the stream of random numbers.
For this code, if we sort as list.sort((a, b) -> a - b);
then, we will not be able to identify what the problem is and where it is happening. That’s why we are sorting it via logging
, which will help us to identify it.
It gives us an error, but it also provides a lot of comparisons of numbers. We will not discuss all, but a few of them are enough to find the error.
OUTPUT:
As we can see, the program violates the consistency rule here, resulting in this error. In the next section, let’s have the solution using Comparator
and Comparable
.
Java Solutions Using the Comparator
and Comparable
Interfaces
Let’s use the Comparator
interface with fewer random numbers.
Example Code:
// 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
This code is executed successfully. We are not going through all the comparisons, but we will see a few of them to confirm.
Check the following screenshot.
OUTPUT:
Let’s learn about the error-free Java code using the Comparable
interface for making comparisons. To use this interface, we have to make a class containing data.
Let’s do it below.
Example Code (Students.java
class):
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;
}
}
}
Example Code (Main.java
class):
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));
}
}
OUTPUT:
Result of Comparison 1: 0
Result of Comparison 2: -1
Result of Comparison 3: 1
In this code, we compare the letter count of firstName
if two objects’ lastName
are the same.
As we can see, the results are consistent. If a==b
, a<b
, and a>b
, it returns 0
, -1
, and 1
, respectively.
Related Article - Java Error
- How to Fix the Error: Failed to Create the Java Virtual Machine
- How to Fix the Missing Server JVM Error in Java
- How to Fix the 'No Java Virtual Machine Was Found' Error in Eclipse
- How to Fix Javax.Net.SSL.SSLHandShakeException: Remote Host Closed Connection During Handshake
- How to Fix the Error: Failed to Create the Java Virtual Machine
- How to Fix Java.Lang.VerifyError: Bad Type on Operand Stack