7. Comparator¶
Comparator is there since jdk 1.2 and we all know its usage and importance. Java-8 came up with few updates to the Comparator as given below.
- Additionl default and static methods were added into
Comparator
interface to support pipeline of stream operations. String
class added withCaseInsensitiveComparator
to sort by ignoring the case.- A new utility class
Comparators
is bundled with jdk-8 to support natural ordered sorting and handlingnull
values while sorting.
7.1. Comparators¶
Comparators is a helper class that provides new Comparator implmentations in the following cases.
- To impose natural ordrered sorting on elements of Comparable types
- Sorting on the collections that mixed with null values.
Natural ordering:
NaturalOrderComparator implements Comparator<Comparable<Object>> {
@Override
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c1.compareTo(c2);
}
}
Comparator
interface contains a static method called naturalOrder
which returns a NaturalOrderComparator
that imposes sorting on elements implementing Comparable
. As you know all wrapper classes for primitive types implements Comparable interface so this natural ordered sorting can be applicable to all of them.
Handling null elements:
Usually comparators throws NullPointerException
if null elements found while performing sorting operation. Comparator
contains two methods nullsFirst and nullsLast that takes a comparator as an argument and returns another null-friendly comparator by wrapping the given comparator. It will arrange null elements at the begining or end depending on the operation you called. For non-null elements it will sort them using the comparator passed initially.
7.2. Updates in Comparator¶
Comparator interface contains some of static methods that returns another comparator implementations described below.
- comparing(Function<T,U> keyExtractor)
This method uses the given key extracting function that applies on T type elements to generate U type comparable sort keys. To compare two elements of type T, it first applies the key extracting function to both the elements and then performs the sorting operation on the resulted keys.
// Sorting words based on word lengths Function<String, Integer> keyExtractor = str -> str.length(); Stream.of("grapes", "milk", "pineapple", "water-melon") .sorted(Comparator.comparing(keyExtractor)) .forEach(System.out::println);
In the above code snippet a
Function<String, Integer> keyExtractor
object is passed to thecomparing
method that in turn will return a Comparator object. It first applied the function to string elements and generated string lengths then returned a comparator definition as given below.Comparator<Integer> c = (s1, s2) -> keyExtractor.apply(s1).compareTo(keyExtractor.apply(s2))
- comparing(Function<T,U> keyExtractor, Comparator<U> keyComparator)
In the first
comparing
method, key extracting function returns sorting keys ofComparable
type so it doesn’t need additional Comparator object to perform sorting. But in thiscomparing
function it first uses the key extracting function to generate key and then performs sorting based on the given comparator.
Stream.of("grapes", "milk", "pineapple", "water-melon") .sorted(Comparator.comparing(String::length, Comparator.reverseOrder())) .forEach(System.out::println);
- comparingXXX(ToXXXFunction<T> keyExtractor)
Comparator interface provides three primitive comparing functions: comparingInt, comparingDouble and comparingLong to sort the elements based on the primitive keys. It accepts ToXXXFunction functional interface which returns primitive values that avoid unnecessary boxing-unboxing costs while doing sorting.
// Natural order sorting by ignoring the sign. Stream.of(-10, 31, 16, -5, 2) .sorted(Comparator.comparingInt(i -> Math.abs(i))) .forEach(System.out::println);
- thenComparing(Comparator<T> other)
It is very much possible that two elements will be equal according to the given comparator. In such cases the other comprator decides the sorting order. Below code snippet shows example of sorting employee objects based on employee’s salary and then uses name if two salaries are equal.
List<Employee> employees = Application.getEmployees(); employees.stream() .sorted(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName)) .forEach(System.out::println);