This is an interesting topic in Java and can be confusing to many Java developers. So I plan to give some common examples to illustrate the differences. Going by the topic name, these things are obvious to us:
- Both comparator and comparable are Java interfaces.
- Any class, which needs to use these interfaces, has to implement methods defined in these interfaces.
- Both are useful in ordering of elements.
But there are some differences in the way comparator and comparable can be used in Java. Lets look at first Comparable and then Comparator interface.
Comparable Interface
public interface Comparable<Object> { public int compareTo(Object obj); }
Following points should be kept in mind when implementing comparable interface:
- Comparable interfaces can be used only for comparing two instances of the same object. Two different objects can’t be used in compareTo() method. This is evident from the compareTo() method as it has only input instance. It would be compared against the current instance on which this compareTo() was invoked.
- Always, compareTo() method must return -1 if current object is lesser than passed in parameter object “obj”, +1 if greater and 0 if equal. This makes sense as any comparison can yield only these logical conclusions. (Greater, equal or lesser). This can also be called natural ordering of elements.
- Comparable exists under java.lang package and Comparator exists under java.util package.
Why should we implement comparable interfaces in Java? Are there any benefits of doing so?
Lets take the following code for example:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ComparableTest { public static void main(String[] args) { List<Double> a = new ArrayList<Double>(); a.add(10.45); a.add(20.45); a.add(30.45); a.add(5.45); a.add(4.55); Collections.sort(a); for(Double d: a) { System.out.println(d); } List<String> s = new ArrayList<String>(); s.add("no3"); s.add("no2"); s.add("no1"); s.add("no4"); Collections.sort(s); for(String str: s) { System.out.println(str); } } }
Code does nothing but sorts the collection in natural order. But if we look through the code, we pass various types to Collection.sort() method. First Double and then Strings but still sort() method works which is awesome.
If we look at the Javadocs, input to sort() method is Comparable a[]. This is where the comparable and comparator interface comes in to play. Because the primitive data types Strings, Double, Integer all implement Comparable Interface, when sort() method is called on these objects, Java internally calls this compareTo() method for doing the comparison with two instances of the same object. Look at the Double class implementation.
public class Double implements Comparable<Double> { public int compareTo(double d1, double d2) { if (d1 < d2) return -1; if (d1 > d2) return 1; return 0; } }
As it can be seen, since Double implements the Comparable interface and overrides the compareTo method, when Collections.sort() method is called with Comparable a[], sort() method internally calls the Double’s compareTo() method. Similarly, String has also implemented Comparable interface and hence Collections.sort() works on Strings also.
A very powerful idea. Using this comparable interface, we were able to make sort() method independent of any data i.e., it can sort any type of object if that particular object implements the Comparable Interface.Might be a little confusing, but run the same program for yourself and then you will realize the usefulness of Comparable interface.
Only basic primitive objects and few other objects implement Comparable Interface. If we want our new Object to be called when sort() method is called, then we need to implement our own Comparable Interface.
Let’s look at an example to illustrate Comparable Interface. A new class ComparableTest implementing the Comparable Interface and overriding the compareTo() method with the logical comparison illustrated earlier.
Client Program:
import java.util.Arrays; public class TestCompareTo { public static void main(String[] args) { ComparableTest[] cmpares = new ComparableTest[3]; ComparableTest one = new ComparableTest(10); ComparableTest two = new ComparableTest(50); ComparableTest three = new ComparableTest(30); cmpares[0] = one; cmpares[1] = two; cmpares[2] = three; Arrays.sort(cmpares); for(ComparableTest test: cmpares) { System.out.println(test.getScores()); } } }
In the client program, I was able to add the new class ComparableTest to Array.sort() method and order scores in natural order because ComparableTest class implements Comparable Interface.
Let’s now moving on to comparator Interface.
Comparator Interface
Having briefly looked at comparable interface, its main shortcomings are:
- Using Comparable interface we can only accomplish the natural ordering of elements, what if we what to sort it with different keys? For eg., Strings ordering can be Case Insensitive ordering, natural ordering.
- Also what if we want a different ordering (descending order) for built in java.lang.Integer, Double class? java.lang.Integer, java.lang.Double can’t be rewritten as these are all final classes. And only natural ordering of elements is possible with Comparable interface. So to achieve our objective of custom ordering of elements and also to order with different keys, we use Comparator Interface.
public interface Comparator<Object> { public int compare (Object item1, Object item2); }
Java in-built examples:
String s1[] = new String[2]; s1[0] = "labEl"; s1[1] = "LabEl2"; Arrays.sort(s1); Arrays.sort(s1, String.CASE_INSENSITIVE_ORDER);
Code above is easy to understand. It orders string array using natural ordering and also with Case Insensitive order. If Strings had used only Comparable Interface, only natural ordering would have been possible. But since String also implements Comparator Interface, it can be ordered using different keys such as Case Insensitive.
Let’s look at an example on how to accomplish this:
import java.util.Comparator; public class ComparatorTest { public static Comparator ASC_ORDER = new AscOrder(); public static Comparator DESC_ORDER = new DescOrder(); private Integer numbers; public ComparatorTest(int num) { this.numbers = num; } public int getNumbers() { return numbers; } public void setNumbers(int numbers) { this.numbers = numbers; } private static class AscOrder implements Comparator { public int compare(ComparatorTest one, ComparatorTest two) { if (one.numbers < two.numbers) { return -1; } else if (one.numbers > two.numbers) { return 1; } return 0; } } private static class DescOrder implements Comparator { public int compare(ComparatorTest one, ComparatorTest two) { if (one.numbers > two.numbers) { return -1; } else if (one.numbers < two.numbers) { return 1; } return 0; } } }
The code implements Comparator Interface and uses two classes to order by two different keys – Ascending and Descending order. Descending order does the opposite ordering of ascending and both classes override the compare() method as part of Comparator Interface. The ASC_ORDER and DESC_ORDER can be used as keys by client program to initiate the appropriate ordering.
Client Program:
import java.util.Arrays; public class TestCompareTo { public static void main(String[] args) { String s1[] = new String[2]; s1[0] = "label"; s1[1] = "label2"; Arrays.sort(s1); Arrays.sort(s1, String.CASE_INSENSITIVE_ORDER); ComparatorTest[] cmpares = new ComparatorTest[4]; ComparatorTest one = new ComparatorTest(10); ComparatorTest two = new ComparatorTest(50); ComparatorTest three = new ComparatorTest(30); ComparatorTest four = new ComparatorTest(20); cmpares[0] = one; cmpares[1] = two; cmpares[2] = three; cmpares[3] = four; Arrays.sort(cmpares, ComparatorTest.ASC_ORDER); for(ComparatorTest test: cmpares) { System.out.println(test.getNumbers()); } Arrays.sort(cmpares, ComparatorTest.DESC_ORDER); for(ComparatorTest test: cmpares) { System.out.println(test.getNumbers()); } } }
As can be seen from the code above, just like String.CASE_INSENSITIVE_ORDER, we were able to achieve two different ordering – ComparatorTest.ASC_ORDER and ComparatorTest.DESC_ORDER. The client can choose any of the ordering it wants to.
Summary
That’s it for Comparable and Comparator interface. Hope this topic is much clearer now than when you started reading this article. Practice some examples to get these concepts clearer. Thanks for reading. Post your comments/critiques in the “comments” section.
This article is originally published at Java tutorials – Lets jump into the ocean, re-posted here with authors permission and as part of the JBC program.