In the section on object equality we showed how we can establish if an object is equal or not to another object via the equals
method. However, we can’t establish if an object is greater than, equal to, or less than another object.
For primitive types we know we can use the relational operators, for example, if we have two int
variables x
and y
, we can check if x
is less than y
by doing x < y
. However, in Java we can’t apply these relational operators to objects.
The Comparable
interface
The standard way in Java to specify that a class offers a way to compare objects is by implementing the Comparable
interface. By implementing this interface, the class is required to implement the following method:
int compareTo(T o);
where T
is the type/class we’re marking as Comparable
. The return type is an int
value that has the following meaning:
-
a negative value means that this object is less than the object passed as a parameter
-
zero means that this object is equal to the object passed as a parameter
-
a positive value means that this object is greater than the object passed as a parameter
When a class implements the Comparable
interface it is said to have a natural order as we can sort objects of this type using this method.
Let’s take a look at an example, where we’ll take the DictionaryEntry
class from the previous sections and add this behavior to it.
Making our DictionaryEntry
a Comparable
type
The first thing we need to do is declare that our DictionaryEntry
class implements the Comparable
interface, and we do this as follows:
public class DictionaryEntry implements Comparable<DictionaryEntry> {
//...
}
The type parameter we pass into the Comparable
interface is <DictionaryEntry>
as we want to compare our DictionaryEntry
class to objects of the same DictionaryEntry
type.
After declaring this, we now need to implement the compareTo
method. For the purpose of this example, let’s assume we want to sort our DictionaryEntry
objects alphabetically on the word
they represent (as you’d find in a dictionary).
public class DictionaryEntry implements Comparable<DictionaryEntry> {
private final String word;
// ...
@Override
public int compareTo(DictionaryEntry o) {
return word.compareTo(o.word);
}
}
In this case, we’re relying on the fact that word
is of type String
and that class also implements Comparable
. Hence, we’re calling compareTo
on the word
field to compare it with the word
that the other object represents.
The compareTo method in the String class in Java can be used to sort Strings in lexicographical order which is what we want for this example.
|
Let’s give this a go:
In the example code run the JavaObjectComparisonApp
|
DictionaryEntry alpha = new DictionaryEntry("alpha", "definition of alpha");
DictionaryEntry beta = new DictionaryEntry("beta", "definition of beta");
System.out.println("Alpha compared to beta: " + alpha.compareTo(beta));
System.out.println("Beta compared to alpha: " + beta.compareTo(alpha));
Output:
Alpha compared to beta: -1
Beta compared to alpha: 1
We can see in the output that when comparing alpha
to beta
we get a negative value, indicating that alpha
is less than beta
. We get the opposite result when comparing beta
to alpha
.
What if I want a different ordering/comparison?
In some cases, you’ll use classes that you don’t own, or that already define a natural order (as in, they already implement Comparable
), but the order defined there isn’t what you’re after.
For example, our DictionaryEntry
classes will sort the objects in lexicographical order, so if you had a list of DictionaryEntry
objects that represented the words: bat
, ant
and ox
, the natural order would result in ant
, bat
, ox
.
However, let’s assume that you want to sort them by length and then, if the words have the same length, sort them in alphabetical order. In this case, the natural order in the DictionaryEntry
won’t work. For the 3 words above, the order we’re after is: ox
, ant
, bat
.
The Comparator
interface
A second commonly used interface is the Comparator<T>
interface that allows us to define an ordering of elements of a given type T
. The main difference, is that the method you need to implement receives 2 objects to be compared to one another:
int compare(T o1, T o2);
The return value has a very similar meaning to the comparable interface:
-
a negative value means that the first object is less than the second object that were passed as parameters
-
zero means that both objects are equal
-
a positive value means that the first object is greater than the second object that were passed as parameters
Now, let’s implement a Comparator
for the use case we discussed above. We’ll call this comparator a WordLengthComparator
:
public class WordLengthComparator implements Comparator<DictionaryEntry> {
@Override
public int compare(DictionaryEntry word1, DictionaryEntry word2) {
int lengthDifference = word1.getWord().length() - word2.getWord().length();
if (lengthDifference == 0) {
return word1.compareTo(word2);
}
return lengthDifference;
}
}
The compare
method is first checking the difference between the length of the words. Only if they are the same length (i.e. if the difference is 0), we rely on the natural order of the words (word1.compareTo(word2)
). If not, we return the lengthDifference
which will be negative if the first word is shorter than the second, or positive otherwise (which is what we want).
We can now use this WordLengthComparator
to do exactly what we wanted for our second sorting example. Let’s give this a go:
In the example code run the JavaObjectComparatornApp
|
DictionaryEntry ant = new DictionaryEntry("ant", "definition of ant");
DictionaryEntry bat = new DictionaryEntry("bat", "definition of bat");
DictionaryEntry ox = new DictionaryEntry("ox", "definition of ox");
WordLengthComparator wordLengthComparator = new WordLengthComparator();
System.out.println("Ox compared to ant: " + wordLengthComparator.compare(ox, ant));
System.out.println("Ox compared to bat: " + wordLengthComparator.compare(ox, bat));
System.out.println("Ant compared to bat: " + wordLengthComparator.compare(ant, bat));
Output:
Ox compared to ant: -1
Ox compared to bat: -1
Ant compared to bat: -1
We can see that the word ox
is considered to be less than ant
and bat
as expected, as it has a shorter length (we get a negative value for both comparisons).
Also, as ant
and bat
have the same length, they are sorted alphabetically and we get that ant
is less than bat
.