How to Fix Comparison Method Violates Its General Contract Error in Java

Mehvish Ashiq Feb 02, 2024
  1. Difference Between Comparator and Comparable Interfaces
  2. Comparison Rules for Comparator and Comparable
  3. Java Code Having the comparison method violates its general contract Error
  4. Java Solutions Using the Comparator and Comparable Interfaces
How to Fix Comparison Method Violates Its General Contract Error in Java

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 and b, the Trichotomy Law must be satisfied, which means exactly one of the following relations must be true:

    • a is less than b (it returns -ve value if a < b)
    • a equals b (it returns 0 if a == b )
    • a is greater than b (it returns +ve value if a > b)
  • It must satisfy the Transitivity, which means if a < b and b < c then, for any three numbers a, b, c, it implies a < c.

  • The third rule is about Antisymmetry, where a < b implies ~b < a

  • Substitutability is also a comparison rule that says, suppose, a == b and a < c; this implies b < 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:

fix comparison method violates its general contract error in java - error

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:

fix comparison method violates its general contract error in java - solution 1

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.

Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook

Related Article - Java Error