Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparable interface depends upon size?

I was working on Comparable interface and found that it is throwing IllegalArgumentException when i put size=50 in the program below and is working fine when i put size =5.

    public class Test {
    public static void main(String[] args) {
        int size = 50;
        Test compareTest = new Test();
        compareTest.test(size);
    }

    public void test(int size) {
        List<TestObject> requests = new ArrayList<TestObject>();
        for (int index = 0; index < size; index++) {
            TestObject request = new TestObject();
            request.value = index;
            requests.add(request);
        }
        Collections.sort(requests);
    }

}

class TestObject implements Comparable<TestObject> {
    public int value;

    public int compareTo(TestObject req) {
        if (value % 3 == 0) {
            return -1;
        } else if (value % 3 == 1) {
            return 0;
        }

        return 1;
    }
}

I am not sure about the root cause of this problem, can some one help me on this.

Exception stack trace is given below.

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:744)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:481)
    at java.util.ComparableTimSort.mergeCollapse(ComparableTimSort.java:406)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:213)
like image 951
Sachin Sachdeva Avatar asked Feb 05 '23 08:02

Sachin Sachdeva


1 Answers

You violate the Comparable contract.

Actually you don't compare two objects between them but you compare only the value field of the current TestObject object according to the result of modulo of 3. You don't use the TestObject object passed as parameter in the compareTo() method.

Suppose you have a List of two TestObject objects with 3 as value field

The two objects will return -1 :

    if (value % 3 == 0) {
        return -1;
    } 

But according to the rule :

The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.)

Assuming the first object is x and the second object is y.
If y.compareTo(x) returns a negative number (such as -1), so x.compareTo(y)should return a positive number (such as 1).

I was working on Comparable interface and found that it is throwing IllegalArgumentException when i put size=50 in the program below and is working fine when i put size =5

In fact when you violate the Comparable contract, the results are not predictable. It may work with some specific values, don't work with other specific values.
It may work in a specific JVM version and don't work in another one.
Trying to understand why it fails or it successes for a specific value may be interesting but it is really not helpful.
Trying to understand the contract in order to respect it is much better. Because today it works is in way but tomorrow in a future implementation, it could change. Only the API is a guarantee.

like image 150
davidxxx Avatar answered Feb 08 '23 22:02

davidxxx