A difference is that "foo".equals((String)null) returns false while "foo".compareTo((String)null) == 0 throws a NullPointerException. So they are not always interchangeable even for Strings.
A difference is that "foo".equals((String)null) returns false while "foo".compareTo((String)null) == 0 throws a NullPointerException. So they are not always interchangeable even for Strings.
The 2 main differences are that:
equalswill take any Object as a parameter, butcompareTowill only take Strings.equalsonly tells you whether they're equal or not, butcompareTogives information on how the Strings compare lexicographically.
I took a look at the String class code, and the algorithm within compareTo and equals looks basically the same. I believe his opinion was just a matter of taste, and I agree with you -- if all you need to know is the equality of the Strings and not which one comes first lexicographically, then I would use equals.
The reason you have two different methods is that they do two different things.
The .equals method returns a boolean value indicating whether the object on which you call the method is equal to the object passed in as a parameter (for some definition of "is equal to" that is consistent with the nature of the object being compared).
The .compareTo method returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. That makes it a useful method for sorting; it allows you to compare one instance with another for purposes of ordering.
When the Java documentation says that these two methods must behave consistently, what they mean is that the .equals method must return true in exactly the same situations where the .compareTo method returns zero, and must return false in exactly the same situations where the .compareTo method returns a nonzero number.
Is there any good reason to violate these rules? Generally no, for the same reasons that
#define TRUE FALSE
is a really bad idea. The only legitimate reason for inconsistent behavior is hinted at in the Java documentation itself: "[if the] class has a natural ordering that is inconsistent with equals."
To drive home the point, you can actually define .equals() in terms of compareTo(), thus guaranteeing consistent behavior. Consider this .equals() method from a Rational class which, after a few sanity checks, simply defines .equals as compareTo() == 0:
public boolean equals(Object y) {
if (y == null) return false;
if (y.getClass() != this.getClass()) return false;
Rational b = (Rational) y;
return compareTo(b) == 0;
}
This confusion would occur in situations where there are conflicting understandings of equals.
compareTo is 'easy' in that it asks a simple question 'which one is bigger?' If you were given a bunch of Things, how do you sort them?
equals on the other hand wants to ask 'are these the same thing?'
BigDecimal in Java is one such place where there are conflicting understandings of what it means to have two things being equal.
BigDecimal foo = new BigDecimal("1.00");
BigDecimal bar = new BigDecimal("1.000");
These two entires will have the same meaning for sorting. They, however, are not the same when it comes to equality. They have a different underlying state (the precision of the number) and that means they are not equal but foo.compareTo(bar) will return 0.
Consider this, if you had a Map qux = HashMap<BigDecimal, Object>() for some reason, do you want qux.put(foo,foo) to take the same spot as qux.put(bar,bar) and thus evict the earlier insertion?
So, while they are math equals (which is how compareTo sorts them), they are not inner state equals, and thus the necessity of the inconsistency here.
Yes, this inconsistency comes at the price of a higher cognitive load for dealing with BigDecimal. It means maps may not behave like you want them to... and the question is "which map do you want to behave 'right'?"
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.TreeMap;
class Main {
public static void main (String[] args) {
BigDecimal foo = new BigDecimal("1.00");
BigDecimal bar = new BigDecimal("1.000");
HashMap<BigDecimal, String> hash = new HashMap();
TreeMap<BigDecimal, String> tree = new TreeMap();
hash.put(foo, "foo");
hash.put(bar, "bar");
tree.put(foo, "foo");
tree.put(bar, "bar");
System.out.println("hash foo: " + hash.get(foo));
System.out.println("hash bar: " + hash.get(bar));
System.out.println("tree foo: " + tree.get(foo));
System.out.println("tree bar: " + tree.get(bar));
}
}
ideone
Output:
hash foo: foo
hash bar: bar
tree foo: bar
tree bar: bar
Because compareTo returned 0, in the TreeMap, bar evicted foo when bar was inserted. However, because these are different objects with different internal state and thus different hash codes, they were both able to exist within a HashMap.
From the docs:
Note: care should be exercised if
BigDecimalobjects are used as keys in a SortedMap or elements in a SortedSet sinceBigDecimal's natural ordering is inconsistent with equals. See Comparable, SortedMap or SortedSet for more information.
And so, thats the problem, the inconsistency and the "there's no good answer to this" dilemma.
One could also imagine this with a Rational class where one wants to keep 2/4 having the internal state of 2/4 so that:
Rational twoFourths = new Rational(2,4);
Rational oneHalf = new Rational(1,2);
System.out.println(twoFourths); // prints 2/4
System.out.println(oneHalf); // prints 1/2
System.out.println(twoFourths.compareTo(oneHalf)); // prints 0
System.out.println(twoFourths.equals(oneHalf)); // ???
And you are once again up against that same question. Are these equal with the mathematical sense of equality (which means the hashCode needs to also return the same value?) or are they not equal using the object oriented state of the object sense of equality despite being the 'same'.
First of all == compares the references to see if the two objects are the same (so == is on the object).
Then String.equals() verify the equality of the content of two strings while String.compareTo() seek the difference of the content of two strings.
So the two following tests are equivalent:
String str = "my string";
if ( str.equals("my second string")) {/*...*/}
if ( str.compareTo("my second string")==0) {/*...*/}
But, since String.equals is making a reference check first, it's safe when used against null, while String.compareTo will throws a NullPointerException:
String str = "my string";
if ( str.equals(null)) {/* false */}
if ( str.compareTo(null) {/* NullPointerException */}
Did you try it?
String a = new String("foo");
String b = new String("foo");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
System.out.println(a.compareTo(b)); // 0
Not all objects can be compared, but all objects can be checked for equality. If nothing else, one can see if two objects exist at the same location in memory (reference equality).
What does it mean to compareTo() on two Thread objects? How is one thread "greater than" another? How do you compare two ArrayList<T>s?
The Object contract applies to all Java classes. If even one class cannot be compared to other instances of its own class, then Object cannot require it to be part of the interface.
Joshua Bloch uses the key words "natural ordering" when explaining why a class might want to implement Comparable. Not every class has a natural ordering as I mentioned in my examples above, so not every class should implement Comparable nor should Object have the compareTo method.
...the
compareTomethod is not declared inObject. ... It is similar in character toObject'sequalsmethod, except that it permits order comparisons in addition to simple equality comparisons, and it is generic. By implementingComparable, a class indicates that its instances have a natural ordering.
Effective Java, Second Edition: Joshua Bloch. Item 12, Page 62. Ellipses remove references to other chapters and code examples.
For cases where you do want to impose an ordering on a non-Comparable class that does not have a natural ordering, you can always supply a Comparator instance to help sort it.
The JLS §4.3.2 defines the class object in the following way:
4.3.2. The Class Object
The class
Objectis a superclass (§8.1.4) of all other classes.All class and array types inherit (§8.4.8) the methods of class
Object, which are summarized as follows:
The method
cloneis used to make a duplicate of an object.The method
equalsdefines a notion of object equality, which is based on value, not reference, comparison.The method
finalizeis run just before an object is destroyed (§12.6).The method
getClassreturns the Class object that represents the class of the object.A
Classobject exists for each reference type. It can be used, for example, to discover the fully qualified name of a class, its members, its immediate superclass, and any interfaces that it implements.The type of a method invocation expression of
getClassisClass<? extends |T|>whereTis the class or interface searched (§15.12.1) forgetClass.A class method that is declared
synchronized(§8.4.3.6) synchronizes on the monitor associated with the Class object of the class.The method
hashCodeis very useful, together with the method equals, in hashtables such asjava.util.Hashmap.The methods
wait,notify, andnotifyAllare used in concurrent programming using threads (§17.2).The method
toStringreturns a String representation of the object.
So, that's why equals is in Object but compareTo is in a separate interface. I would speculate that they wanted to keep Object as minimal as possible. They probably figured that nearly all Objects would need equals and hashCode (which is really just a form of equality testing) but not all objects would need to have a concept of ordering, which is what compareTo is used for.
While it is recommended (and pretty sensible) that having a.compareTo(b) == 0 imply that a.equals(b) (and visa versa), it is not required. Comparable is intended to be used when performing an ordering on a series of objects, whereas equals() just tests for straight equality.
This link has some good information on implementing compareTo properly.
From Javadoc of java.lang.Comparable:
It is strongly recommended (though not required) that natural orderings be consistent with equals.
Ex:
(name1.compareTo(name2) == 0 ) //if this is true, name1 will be the same as name2
//or should I use
(name1.equals(name2)) { //if they are equal, they will also be the same.
since both examples could work...which one should I use? and what is the difference between them?