The Comparable Interface
Collections.sort() is a static method of the java.util.Collections class, and it sorts a list into ascending order when called with a single argument.
Predicting a Sort Before Reading the Rules
Imagine a program that builds a list of words, asks Java to put them in order, and then prints the list. Sorting looks like a single step, but it touches several parts of the language at once: the list, the items inside it, and a utility method that does the work. This article approaches the topic from the outside in. You will first see what happens when a list is sorted, then learn which rules govern the order, and finally learn how to change that order with a Comparator.
What do you think happens?
Suppose a list contains three Strings, added in the order pear, apple, mango, and then Collections.sort() is called on it with a single argument. What order will the items be in afterward?
Reveal answer
apple, mango, pear
A single-argument Collections.sort() puts the list into ascending order using the natural ordering of the items. For Strings, that natural ordering is alphabetical, so apple comes first, mango second, and pear last.
As you read, keep one question in mind: when the sorted output is not what you expected, where in the chain of steps did the result diverge? Learning to answer that question is the real skill this article builds.
A Sort Call Without a Sort Method
A natural first guess is that the List interface would provide a sort method, since sorting a list seems like a basic operation. It does not. The likely reason is that sorting only makes sense for certain kinds of objects, so the designers of the interface left it out. Instead, the sorting methods are static methods of a separate class, java.util.Collections.
Sorting a list is therefore a call on the Collections class rather than a method you invoke on the list itself. You pass the list in as an argument. If you have used the Arrays class to sort arrays, the idea is similar: a helper class supplies the operation, and you supply the data.
Collections.sort() is a static method of the java.util.Collections class. With a single argument, the list, it sorts the list into ascending order. The list is the thing being rearranged; the Collections class is the helper that performs the work.
Natural Ordering and the Comparable Rule
Ascending order means the smallest item comes first and the largest comes last, using the natural ordering of the items. For the single-argument call to work, the items in the list must implement the Comparable interface.
The source material gives two families of examples where this holds: Strings, and wrapper classes such as Integer and Double. Those types already know how to compare themselves, so Collections.sort() can rely on that knowledge. A Comparable item carries its own ordering rule, which is why the single-argument form needs nothing else from you.
Tracing a Single-Argument Sort
Here is the single-argument form in action. The list starts with pear, apple, and mango. After the call, the same list object holds the items in ascending order.
[apple, mango, pear]List.ofcreates the starting list with the three Strings in the order pear, apple, mangoCollections.sort(fruits)is the single-argument form; it passes only the list- The printed output shows the list after sorting, in ascending order
The before row shows the list as it was added, and the after row shows the same three positions once the sort finishes. Each position now holds a different word. The list object itself was rearranged, which is why printing it afterward shows the sorted order.
Changing the Order with a Comparator
Sometimes the natural ordering is not the one you want, or the items do not implement Comparable at all. For those cases there is a second form of the call that takes a Comparator as its second argument. When a comparator is supplied, Collections.sort() uses it to compare items in the list, instead of relying on the items' own natural ordering.
The comparator is a separate object, so the ordering rule lives outside the items. This is what makes the second form useful for custom orders. The same items can be sorted in different ways by passing different comparators, without changing the items themselves.
Consider a list of words that should be ordered by how many characters each word has, rather than alphabetically. A Comparator can express that rule. The example below is an original illustration written for this article, not taken from the source. It defines a comparator whose compare() method compares the lengths of two Strings.
[fig, kiwi, banana]- The Comparator object holds the ordering rule, separate from the Strings
compare()receives two words and returns a numberCollections.sort(words, byLength)is the two-argument form; the comparator replaces natural ordering
Reading the Return Value of compare()
Read the compare() method as the rule. It receives two words and returns a number. The sign of that number tells the sort how the two items relate. Here the rule says that a shorter word comes before a longer one. Collections.sort() calls this rule on pairs of items from the list, so the rule, not the items' natural ordering, decides the final arrangement.
| Return value | Meaning for the sort | Example with byLength |
|---|---|---|
| Negative | The first item belongs earlier | fig compared with banana gives 3 minus 6, which is negative, so fig goes first |
| Zero | The two items are treated as equal for ordering | Two words of the same length give 0 |
| Positive | The first item belongs later | banana compared with fig gives 6 minus 3, which is positive, so banana goes later |
How the sign of the value returned by compare() guides the sort
The sequence diagram shows the two-argument route. The calling code makes one call to Collections.sort(). The sort then repeatedly asks the comparator to compare pairs of items, and it uses each returned sign to decide the arrangement. The comparator's compare() method is therefore the place where the ordering rule is applied, while Collections.sort() handles the rearranging.
Mistakes That Break the Sort
Common mistake Calling the single-argument form on a list of objects that do not implement Comparable
What it looks likeCollections.sort(listOfCustomObjects) where the class never implements Comparable
Why it failsThe single-argument form relies on each item's natural ordering, and an item without Comparable has none to supply.
Do this insteadMake the class implement Comparable, or pass a Comparator as the second argument to supply the rule.
Common mistake Expecting the comparator to be ignored when it is passed
What it looks likePassing byLength but expecting alphabetical output
Why it failsWhen a comparator is supplied,
Collections.sort()uses it instead of the items' natural ordering.Do this insteadCheck which form of the call was used, because the second argument decides the ordering rule.
Common mistake Assigning the result of Collections.sort() to a new variable
What it looks likeList<String> sorted = Collections.sort(words);Why it failsCollections.sort()rearranges the list passed to it and does not return a new sorted list to store.Do this insteadCall Collections.sort(words) and then use words, which now holds the sorted order.
Common mistake Thinking the List interface has its own sort method, when sorting is a static call on Collections
Diagnosing an Unexpected Order
A developer writes Collections.sort(people) on a list of Person objects. The class Person does not implement Comparable, and the developer expects the list to end up sorted by surname. Using the three-checkpoint method, identify which checkpoint fails and describe the two fixes that would make the call behave as intended.
Hints
- Start by asking which form of the call was used
- Ask whether the items supply a natural ordering
- Consider whether a comparator that compares surnames could be passed as the second argument
Write a comparator that orders the words fig, banana, and kiwi from longest to shortest, then predict the printed output before running the code. Explain which sign of the return value puts the longer word first.
Hints
- Reverse the subtraction used in the
byLengthexample - A negative return value means the first item belongs earlier
Key Takeaways
Collections.sort()is a static method of thejava.util.Collectionsclass, and with a single argument it sorts a list into ascending order.- The single-argument form works when the items implement the Comparable interface, as Strings and wrapper classes such as Integer and Double do.
- Passing a Comparator as the second argument supplies the ordering rule through its
compare()method, replacing the items' natural ordering. - A negative, zero, or positive return value from
compare()tells the sort whether the first item belongs earlier, the two are equal for ordering, or the first item belongs later. - When a sort result is wrong, check which form of the call was used, whether the items are Comparable, and what the comparator returns.