Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does compareTo return an integer

Tags:

I recently saw a discussion in an SO chat but with no clear conclusions so I ended up asking there.

Is this for historical reasons or consistency with other languages? When looking at the signatures of compareTo of various languages, it returns an int.

Why it doesn't return an enum instead. For example in C# we could do:

enum CompareResult {LessThan, Equals, GreaterThan}; 

and :

public CompareResult CompareTo(Employee other) {     if (this.Salary < other.Salary) {          return CompareResult.LessThan;     }     if (this.Salary == other.Salary){         return CompareResult.Equals;     }     return CompareResult.GreaterThan; } 

In Java, enums were introduced after this concept (I don't remember about C#) but it could have been solved by an extra class such as:

public final class CompareResult {     public static final CompareResult LESS_THAN = new Compare();     public static final CompareResult EQUALS = new Compare();     public static final CompareResult GREATER_THAN = new Compare();      private CompareResult() {} }   

and

interface Comparable<T> {     Compare compareTo(T obj); } 

I'm asking this because I don't think an int represents well the semantics of the data.

For example in C#,

l.Sort(delegate(int x, int y)         {             return Math.Min(x, y);         }); 

and its twin in Java 8,

l.sort(Integer::min); 

compiles both because Min/min respect the contracts of the comparator interface (take two ints and return an int).

Obviously the results in both cases are not the ones expected. If the return type was Compare it would have cause a compile error thus forcing you to implement a "correct" behavior (or at least you are aware of what you are doing).

A lot of semantic is lost with this return type (and potentially can cause some difficult bugs to find), so why design it like this?

like image 219
user2336315 Avatar asked Mar 30 '15 09:03

user2336315


People also ask

How does compareTo work for integers?

compareTo() method compares two Integer objects numerically. This method returns the value 0 if this Integer is equal to the argument Integer, a value less than 0 if this Integer is numerically less than the argument Integer and a value greater than 0 if this Integer is numerically greater than the argument Integer.

What does the compareTo method return?

Java String compareTo() Method The method returns 0 if the string is equal to the other string. A value less than 0 is returned if the string is less than the other string (less characters) and a value greater than 0 if the string is greater than the other string (more characters).

What datatype that the function compareTo will return?

compareTo() Return Values compareTo() in java returns an integer value. It returns a positive integer if string1 is lexicographically greater than string2, negative if string2 is greater than string1, and zero if both are equal.

Will two object always be equal when their compareTo () method returns zero?

Now, if Comparable returns Zero, it means two objects are the same by comparison. If two objects are the same by using the equals method, it returns true.


1 Answers

[This answer is for C#, but it probably also apples to Java to some extent.]

This is for historical, performance and readability reasons. It potentially increases performance in two places:

  1. Where the comparison is implemented. Often you can just return "(lhs - rhs)" (if the values are numeric types). But this can be dangerous: See below!
  2. The calling code can use <= and >= to naturally represent the corresponding comparison. This will use a single IL (and hence processor) instruction compared to using the enum (although there is a way to avoid the overhead of the enum, as described below).

For example, we can check if a lhs value is less than or equal to a rhs value as follows:

if (lhs.CompareTo(rhs) <= 0)     ... 

Using an enum, that would look like this:

if (lhs.CompareTo(rhs) == CompareResult.LessThan ||     lhs.CompareTo(rhs) == CompareResult.Equals)     ... 

That is clearly less readable and is also inefficient since it is doing the comparison twice. You might fix the inefficiency by using a temporary result:

var compareResult = lhs.CompareTo(rhs);  if (compareResult == CompareResult.LessThan || compareResult == CompareResult.Equals)     ... 

It's still a lot less readable IMO - and it's still less efficient since it's doing two comparison operations instead of one (although I freely admit that it is likely that such a performance difference will rarely matter).

As raznagul points out below, you can actually do it with just one comparison:

if (lhs.CompareTo(rhs) != CompareResult.GreaterThan)     ... 

So you can make it fairly efficient - but of course, readability still suffers. ... != GreaterThan is not as clear as ... <=

(And if you use the enum, you can't avoid the overhead of turning the result of a comparison into an enum value, of course.)

So this is primarily done for reasons of readability, but also to some extent for reasons of efficiency.

Finally, as others have mentioned, this is also done for historical reasons. Functions like C's strcmp() and memcmp() have always returned ints.

Assembler compare instructions also tend to be used in a similar way.

For example, to compare two integers in x86 assembler, you can do something like this:

CMP AX, BX ;  JLE lessThanOrEqual ; jump to lessThanOrEqual if AX <= BX 

or

CMP AX, BX JG greaterThan ; jump to greaterThan if AX > BX 

or

CMP AX, BX JE equal      ; jump to equal if AX == BX 

You can see the obvious comparisons with the return value from CompareTo().

Addendum:

Here's an example which shows that it's not always safe to use the trick of subtracting the rhs from the lhs to get the comparison result:

int lhs = int.MaxValue - 10; int rhs = int.MinValue + 10;  // Since lhs > rhs, we expect (lhs-rhs) to be +ve, but:  Console.WriteLine(lhs - rhs); // Prints -21: WRONG! 

Obviously this is because the arithmetic has overflowed. If you had checked turned on for the build, the code above would in fact throw an exception.

For this reason, the optimization of suusing subtraction to implement comparison is best avoided. (See comments from Eric Lippert below.)

like image 134
Matthew Watson Avatar answered Oct 24 '22 19:10

Matthew Watson