Tip:
Highlight text to annotate it
X
Hi. Welcome to the Vidya tutorial
Comparison Shopping. This tutorial is for intermediate-level
Java developers who want to add comparison
and sorting capabilities to the custom classes
they’ve created for their projects.
So let’s get started. The files used in this tutorial
are available on Github at the link provided. Please
download them if you would like to follow along.
When you need sorted collections of objects in
your Java applications, if a database is available,
you can run queries returning ordered results,
which you can then convert into an automatically
ordered collection of Java objects. That’s an easy,
often high-performing solution. But if that isn’t an option
for whatever reason, you need to write code for objects
to know how to compare themselves with each other in memory.
To learn how to do this, it’s helpful to look at core
Java classes as a model. For example, let’s see how
things work with String in our Comparison Shopper class.
The code is really simple. We create a generic
ArrayList of String and add three strings representing
makes of car--Mercedes, Audi, and BMW--in that order.
Then we sort the list and print out the results. Let’s run the code
and see what happens.
As you can see, the list is sorted alphabetically as you would
expect. It just worked. But how? To get answers like this, you
always need to look at the JavaDocs available online or through
your IDE. Let’s take a look at the JavaDoc for String.
Check out the interfaces String implements.
Comparable String. That looks relevant. Let’s see what that is..
Comparable looks pretty popular with a lot of
implementing classes. According to the JavaDoc,
Comparable “imposes a total ordering on the objects of
each class that implements it.” So we need to implement
the compareTo method in Comparable.
The JavaDoc specification for compareTo has typically
cumbersome wording, but basically you compare the
current object with the one passed in as a parameter. If the
current object is bigger, return 1; if it’s smaller, return negative 1;
if they’re equal, return 0. That’s it.
Notice also that because Comparable is generic, you have a
compile-time guarantee that the parameter to compareTo will be
the same type as your object. If you even tried to mess with it
because you are really bored, you would get errors when you
compile and maybe even immediately in your editor. Getting feedback
before runtime saves you a lot of time, which is helpful when you
have deadlines at work. So Strings are sortable out of the box
because String implements the compareTo method in Comparable.
Let’s do the same thing with our own custom class.
Here is a very simple class Car with just a make and a price.
Since we want to be able to sort a collection of cars, we implement
Comparable on Car, which means we need to implement a
compareTo method with a Car parameter. Let’s look at that method.
The first thing we do is some error checking. As a practical
matter, a null parameter or a mistyped parameter due to
classloading issues will probably never happen, but if you go back
and look, the specification in the JavaDoc for compareTo
describes how you should handle weird cases like these.
Just as we have here, always honor the spec. Anyway, the
rest of the method is much more interesting.
Since we already did some string sorting, this time let’s decide
we want our cars to be sorted on price from lowest to highest. If the
price of this car is less than the price of the car in the parameter,
we return negative 1; if it’s greater, we return 1. If we are still here,
the two prices must be equal, so we return 0. Pretty easy. Let’s
go back to Comparison Shopper to see how it all works.
Similar to before, we create an ArrayList--this time, of
Car objects, and sort it. Looking at the prices of these
elite makes, which make me sad for obvious reasons,
we expect the order to be BMW, Audi, Mercedes,
from low to high price.
And that’s what we see.
So there you have it. By implementing Comparable, the
Car class is now sortable. That wasn’t too tough. This is
enough to get by most of the time, so you are welcome to
quit watching and play with the code. But we have to cover
two more topics--consistency with equals and alternate
comparisons--so you can have the elite understanding that
will make you the envy of the cube farm and a hit at parties.
On consistency with equals, let’s go back to the JavaDoc for
Comparable.
Expert Java developers know they are supposed to override
equals (and hashCode for that matter) for all their custom
classes. As you can see, the JavaDoc has the usual complicated
wording, but all it says is that if two Comparable objects are
equal as calculated by the equals method, then compareTo
with the same two objects should return 0...and vice versa. Let’s
go back to the code for Car.
Here are compareTo again and equals. As you can see, once
we get past all the equals method boilerplate code you have to
have, we define equality with a compareTo call returning 0. Note
the explicit cast of the Object parameter to type Car, which we
can safely do after passing the checks above. So compareTo
and equals are consistent.
This isn’t required, and you might be able to get away with being
inconsistent, but as the JavaDoc points out, a lot of weird things
can happen with core Java collections like SortedSet and
SortedMap. Also, who knows what might happen with collections
found in third-party libraries like Google Guava and Apache
Commons Collections? Spare yourself the debugging on the
weekend, and just take the 5 minutes to get this right.
The last thing we need to cover is alternate comparisons. We use
Comparable to define what’s called the natural order of a class.
In other words, how we want things to be sorted by default.
For Strings, the natural order is alphabetical. For our Cars,
the natural order is by price. What if that works most of the time,
but there are some cases where we need to sort on something
different than the natural ordering? Like sorting a list of Strings
by length every now and then. Or sorting our cars by make.
For situations like this, we use a Comparator.
While Comparable is in the java.lang package, Comparator is an
interface in the java.util package, which means you need to import
it explicitly. But as you can see from the JavaDoc, it looks a lot like
Comparable. It is an interface with a generic comparison method
and the same contractual obligations.
The key differences are the name of the method compare and
that it takes two generically typed parameters. A separate class
entirely, not String or Car, implements Comparator and gets
passed along when it comes time to sort a collection.
Let’s see how this works.
Make Comparator is a class whose sole purpose is to enable us
to sort cars on make while Car retains its natural ordering on price.
compare takes the two car parameters and first handles some edge
cases that will probably never happen. But then it does a neat thing.
Since the make of a Car is a string, and strings implement Comparable,
we can actually take advantage of the natural ordering of Strings
to achieve an alternate ordering for Car by make. This makes
for a simple, one-line comparison. To use Make Comparator,
let’s go back to Comparison Shopper.
We create the same list of cars, but this time we call an overloaded
version of Collections.sort where we pass in the collection
AND an instance of Make Comparator. When the sort method
sees this, it knows to ignore the natural ordering on Car defined
by compareTo and to use the ordering defined by Make Comparator
instead. Let’s run the code and see what happens
You can see the difference when sorting on natural order
and sorting with a Comparator. And with that, you are now an
expert on creating your own Java objects that can be sorted
within any kind of collection on any dimension.
Let’s summarize. In order to make your custom Java classes
sortable, they need to implement the Comparable interface.
The compareTo method in Comparable is where you define
the class’ natural ordering--what makes one instance of your
class less than, equal to, or greater than another. It’s up to you
what the natural order should be for your class.
Take the time to implement compareTo exactly as the
specification states to avoid nasty problems later even though
you might be able to get away without doing it. If you need
to be able to sort your objects in a different way beyond the
natural ordering, write a class that implements Comparator
and pass an instance to the Collections dot sort method.
If you haven’t already, don’t forget to download the files used
in this tutorial from Github and explore the code for yourself.
You will never learn technology listening to someone talk.
Getting your hands dirty is the only way to do it.
We really hope you enjoyed this tutorial and learned enough to
add comparison and sorting to your Java code. Java is a
rich language with a lot of terrific features, and we will
explore many of them in greater detail in future tutorials
and blog posts on the Vidya website.
We hope you will check those out too.
Thanks for watching. This has been Comparison Shopping by Vidya.