The Comparator 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.
A List That Puts Itself in Order
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.
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.
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 are the items in afterward?
Reveal answer
apple, mango, pear
With a single argument, Collections.sort() puts the items into ascending order using their natural ordering. For Strings, that natural ordering is alphabetical, so apple comes first, then mango, then pear.
Why Sorting Belongs to Collections
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, and it sorts a list into ascending order when called with a single argument.
Sorting With One Argument
The simplest form of the call takes one argument, the list. Its job is to sort the list into ascending order. Here is that form applied to the three words from the opening prediction.
[apple, mango, pear]- The list is created and filled in the order pear, apple, mango.
Collections.sort(fruits)is the single-argument form: only the list is passed in.- The list itself is rearranged; the Collections class does the work.
Trace what changed. Before the call, the list held the items in insertion order. After the call, the same list object holds them in ascending order. The printed output reflects that change, because the list was rearranged in place rather than copied into a new list.
What Ascending Order Means
The phrase ascending order means the smallest item comes first and the largest comes last, using the natural ordering of the items. For this 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 the wrapper classes such as Integer and Double. Those types already know how to compare themselves, so Collections.sort() can rely on that knowledge.
When the Natural Order Is Not Enough
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.
Notice the division of responsibility. The list is still the thing being rearranged. The items are still the things being compared. What changes is the source of the rule that decides which of two items belongs earlier.
Sorting Words by Length
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]- 1Line 3 declares
byLengthas a Comparator for Strings. The anonymous class supplies the one method the interface requires. - 2Line 5 defines
compare(String a, String b). It receives two words from the list. - 3Line 6 subtracts the lengths. For fig (3) and kiwi (4) the result is negative, so fig is placed before kiwi.
- 4Lines 9 to 12 build the list in its starting order: banana, fig, kiwi.
- 5Line 14 passes the list and the comparator to
Collections.sort(). The comparator now governs every comparison the sort makes.
byLengthis a Comparator object whosecompare()method holds the ordering rule.compare(a, b)returns a negative number when a is shorter than b, so the shorter word is placed earlier.Collections.sort(words, byLength)is the two-argument form: the list and the comparator are both passed in.- Alphabetical order would have produced [banana, fig, kiwi]; the comparator changed the result.
Reading the Return Value of compare()
Read the compare() method as the rule. It receives two words and returns a number. A negative result means the first word belongs earlier, a positive result means it belongs later, and zero means the two are treated as equal for ordering. 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 of compare(a, b) | Meaning for ascending order | Result for the list |
|---|---|---|
| Negative | a belongs earlier than b | a is placed before b |
| Zero | a and b are treated as equal | neither is placed before the other by this rule |
| Positive | a belongs later than b | a is placed after b |
The three possible results of a comparison, read as ascending-order decisions.
Where compare() Gets Called
The diagram below traces the calls during a two-argument sort. The caller hands the list and the comparator to Collections.sort(). Collections repeatedly asks the comparator to compare pairs of items taken from the list, reads the sign of each returned value, and rearranges the list accordingly. The comparator never touches the list directly; it only answers questions about two items at a time.
The sequence repeats: Collections picks pairs, the comparator returns a sign, and the list is rearranged, until the sort is finished. This is why the comparator's compare() method is the single place where the custom order is decided. Changing that method changes every placement the sort makes.
Choosing Between the Two Forms
The choice between the two forms follows one question: does the ordering come from the items, or from a rule you supply? The flowchart below shows the decision path. With one argument the ordering comes from the items, which must implement Comparable. With a second argument the ordering comes from the Comparator object that Collections.sort() is given. In both cases the list is the thing being rearranged.
Comparable and Comparator Side by Side
The names Comparable and Comparator are easy to confuse. The table below separates them by where the ordering logic lives and how each is supplied to sort().
| Aspect | Comparable | Comparator |
|---|---|---|
| Where the rule lives | Inside the item's class | In a separate object outside the items |
| How it reaches sort() | Implicitly, through Collections.sort(list) | Explicitly, as the second argument |
| Typical use | The natural ordering of Strings and wrapper classes | A custom order such as word length |
| Typical method used by sort() | The item's own comparison | compare() on the supplied object |
Comparable supplies the natural order; Comparator supplies an order chosen by the caller.
Mistakes When Sorting
Common mistake Calling the list's own sort method
Why it failsThe source explains that the List interface does not provide a sort method. Sorting is a static method of
java.util.Collections.Do this insteadPass the list into Collections.sort(list) or Collections.sort(list, comparator).Common mistake Expecting a single-argument sort on objects that are not Comparable
What it looks likeA list of a custom class whose items do not implement Comparable, sorted with Collections.sort(list).
Why it failsThe single-argument form relies on the items' natural ordering, which exists only when the items implement Comparable.
Do this insteadMake the items Comparable, or pass a Comparator as the second argument.
Common mistake Assuming a comparator is ignored when it is passed
Why it failsA comparator replaces the items' natural ordering for that sort.
Do this insteadRemember that the comparator's compare() method decides the order, even for Strings that would otherwise sort alphabetically.
Common mistake Reversing the meaning of the compare() sign
What it looks likeReturning a positive value for a shorter word when the goal is shorter-first.
Why it failsA negative result means the first argument belongs earlier, so the sign controls direction.
Do this insteadReturn a negative value when the first word should come first, such as a.length() - b.length() when shorter words should come first.
Checking an Unexpected Order
When a sort produces an unexpected order, you can work backward through the questions this article has raised. Work through three checkpoints in order. First, identify which form of the call was used: the single-argument form or the two-argument form with a comparator. Second, if the single-argument form was used, check whether the items implement Comparable. Third, if a comparator was used, check what its compare() method returns for pairs of items. The divergence usually appears at one of these three checkpoints, because each one controls a different part of the ordering.
- 1Identify the form: one argument, or a list plus a comparator.
- 2For one argument, confirm the items implement Comparable.
- 3For a comparator, read
compare()and check the sign it returns for a pair of items. - 4Compare the result to the expected order and locate the first checkpoint where the two disagree.
Practice Changing the Order
Using the words banana, fig, and kiwi, write a comparator that orders them from longest to shortest. Predict the output before you run the sort, then check your prediction by printing the list after Collections.sort(words, yourComparator).
Hints
- Start from the
byLengthexample and reverse which word is considered first. - Think about which sign the result should have when the first word is longer.
- Predicted output for longest to shortest: [banana, kiwi, fig].
Key Takeaways
Collections.sort()is a static method ofjava.util.Collections, and a single-argument call sorts a list into ascending order.- The single-argument form works when the items implement Comparable, 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
compare()result places the first item earlier, a positive result places it later, and zero treats the two as equal for ordering. - When a sort is wrong, check the form of the call, whether the items are Comparable, and what the comparator returns.