Implementing the java.util.Comparator Interface 2 – Object Comparison
The String class implements the Comparable<E> interface, providing an implementation of the compareTo() method. The compareTo() method defines the natural ordering for strings, which is lexicographical. The natural ordering is used to maintain the elements sorted lexicographically when the sorted set at (1a) is used. If we wish to maintain the strings in a different ordering, we need to provide a customized comparator.
The String class provides a static field (CASE_INSENSITIVE_ORDER) that denotes a comparator object with a compare() method that ignores the case when comparing strings lexicographically. This particular total ordering is used to maintain the elements sorted when the sorted set at (1b) is used. The comparator is passed as an argument to the set constructor. The output shows how the elements are maintained sorted in the set by this total ordering, which is a case-insensitive ordering.
We can create a string comparator that enforces rhyming ordering on the strings. In rhyming ordering, two strings are compared by examining their corresponding characters at each position in the two strings, starting with the characters in the last position. First the characters in the last position are compared, then those in the last but one position, and so on. For example, given the two strings “report” and “court”, the last two characters in both strings are the same. Continuing toward the start of the two strings, the character ‘o’ in the first string is less than the character ‘u’ in the second string. According to rhyming ordering, the string “report” is less than the string “court”.
Comparing two strings according to rhyming ordering is equivalent to reversing the strings and comparing the reversed strings lexicographically. If we reverse the two strings “report” and “court”, the reversed string “troper” is lexicographically less than the reversed string “truoc”.
A rhyming ordering comparator is implemented by the lambda expression at (1c) in Example 14.10. The lambda expression first creates reversed versions of the strings passed as arguments. A reversed version of a string is created using a string builder, which is first reversed and then converted back to a string, as shown at (2). The compareTo() method call at (3) compares the reversed strings, as the lexicographical ordering for the reversed strings is equivalent to the rhyming ordering for the original strings. This particular total ordering is used to maintain the elements sorted when the sorted set at (1c) is used. The lambda expression is passed as an argument to the set constructor, and is executed when the compare() method of the Comparator<String> interface is called by the sorted set implementation. The output shows how the elements are maintained sorted in the set by this total ordering, which is rhyming ordering.
Finally, a conditional comparator is composed at (4) and (5) that first compares the strings by their length, and if the lengths are equal, it employs the natural ordering for strings. The output shows that the strings are first sorted according to their lengths, and strings with equal lengths are sorted according to their natural ordering.
Example 14.11 illustrates using a comparator that orders version numbers (Example 14.8, p. 763) according to their reverse natural ordering. The method Comparator.reversedOrder() readily returns a comparator that imposes the reverse of the natural ordering.
A list of version numbers is initialized at (2). This list is sorted using the reverse natural ordering at (3). A binary search is done in this list at (4). We have used the same comparator for the search as we did for the sorting, in order to obtain predictable results. Searching this list with natural ordering at (5) does not find the key.
Example 14.11 Using a Comparator for Version Numbers
import static java.lang.System.out;
import java.util.*;
public class UsingVersionNumberComparator {
public static void main(String[] args) {
VersionNumber[] versions = new VersionNumber[] { // (1)
new VersionNumber(3, 49, 1), new VersionNumber(8, 19, 81),
new VersionNumber(2, 48, 28), new VersionNumber(10, 23, 78),
new VersionNumber(9, 1, 1) };
List<VersionNumber> vnList = new ArrayList<>();
Collections.addAll(vnList, versions); // (2)
out.println(“List before sorting:\n ” + vnList);
Collections.sort(vnList, Comparator.reverseOrder()); // (3)
out.println(“List after sorting according to ” +
“reverse natural ordering:\n ” + vnList);
VersionNumber searchKey = new VersionNumber(9, 1, 1);
int resultIndex = Collections.binarySearch(vnList, searchKey,
Comparator.reverseOrder()); // (4)
out.printf(“Binary search in list using reverse natural ordering”
+ ” found key %s at index: %d%n”, searchKey, resultIndex);
resultIndex = Collections.binarySearch(vnList, searchKey); // (5)
out.printf(“Binary search in list using natural ordering”
+ ” found key %s at index: %d%n”, searchKey, resultIndex);
}
}