4

This might be a trivial question, but I have not found anything about it, so here goes:
When implementing the Comparable interface, we are supposed to define the method compareTo(), so that the following is true according to the documentation:

  • sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y.

  • The relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.

  • x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.

Now, the part that gets confusing is the return value, that is specified as follows:

Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

It seems that most implementations return 1, -1 or 0, even though it is not mentioned as a requirement that the return value is limited in this way.
The following code thus works for Sorting a list (using Collections.sort()) containing instances of a class, Foo:

public int compareTo(Foo other){
    return this.value > other.value? 1 : this.value < other.value ? -1 : 0;
}

This however do not:

public int compareTo(Foo other){
    return (int)(this.value - other.value);
}

Where value is a long, and the difference between the values do not exceed Integer.MAX_VALUE.

Am I missing something here, or is the return value required to be exactly 1, -1 or 0, contradictory to the documentation?

Update: Thanks for all your answers, but it seems that the human factor was to blame here. I mentioned that the calculated difference was less than Integer.MAX_VALUE, which should mean that there is no overflow, but my calculations were wrong, so I did actually get overflow, which caused the strange results.

Jave
  • 31,598
  • 14
  • 77
  • 90

3 Answers3

8

The contract was so flexible to allow this.value - other.value idioms (which later turned out to be incorrect due to integer overflow). But in some cases it is still valueable, like str.length() - str2.length(). It is unlikely impossible that string or array size comparison will overflow since the minimum length is 0 and the maximum is Integer.MAX_VALUE (0 - Integer.MAX_VALUE is still greater than Integer.MIN_VALUE) so it is convenient when you need to sort by length/size.

Also comparing to 0 (greater-than/less-than) is often faster/genrates smaller bytecode/assembly than comparing to 1/-1, so why limit the users? You are absolutely free to use any positive/negative value.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
1

Nope, you can return any integer you want. What exactly is the error you are getting?

Please test the following class:

  public static void main(String[] args) {

        final ToSort sort0 = new ToSort(-100);
        final ToSort sort1 = new ToSort(1);
        final ToSort sort2 = new ToSort(100);


        List<ToSort> elements = new ArrayList<ToSort>(){{add(sort2); add(sort1); add(sort0);}};
        System.out.println("Unsorted:" + elements.toString());

        Collections.sort(elements);

        System.out.println("Sorted:" + elements.toString());

    }

    static class ToSort implements Comparable{

        long value;
        public ToSort(long value){
            this.value = value;
        }

        @Override
        public int compareTo(Object other) {
            return (int) (this.value - ((ToSort)other).value);
        }

        public String toString(){
            return ""+value;
        }
    }

I get the following output:

run:
Unsorted:[100, 1, -100]
Sorted:[-100, 1, 100] 
BUILD SUCCESSFUL (total time: 0 seconds)

You might want to add a breakpoint in your compareTo method and debug to check if you are running what you are expecting.

Marcelo
  • 4,580
  • 7
  • 29
  • 46
  • No error, the list is just not sorted property. The double return was a copy-paste mistake. – Jave Feb 01 '12 at 11:53
  • `new Long(1).longValue()` is `1L` so `(int)(new Long(1).longValue() - other)` is `1 - (int) other` – Peter Lawrey Feb 01 '12 at 12:45
  • @PeterLawrey indeed it is, that was just for demonstration purposes - already edited, but `(int) (long-long)` gives the same results as `((int) long) - ((int) long)`, right? – Marcelo Feb 01 '12 at 12:56
  • Thank you for your answer, the problem I was experiencing was due to the large difference between the long-values causing integer overflow. – Jave Feb 01 '12 at 13:03
  • 1
    @Jave Can you give an example of this? I am betting you can't. ;) – Peter Lawrey Feb 01 '12 at 13:34
  • 1
    @PeterLawrey: Seems you win the bet. I thought that casting a long to integer (or any other primitive cast to fewer bits) just truncated the results. Learning so much today! :p – Jave Feb 01 '12 at 14:11
-1

Javadocs also say:

In the foregoing description, the notation sgn(expression) designates the mathematical signum function, which is defined to return one of -1, 0, or 1 according to whether the value of expression is negative, zero or positive.

EDIT:

Yes, i misunderstood. you are right.

Azodious
  • 13,752
  • 1
  • 36
  • 71