The Comparator interface is meant for comparing and sorting objects in collections.
- Prior to Java8, Comparator had only two methods
compare()
andequals()
. But, The enhanced comparator after Java8 now contains 19 methods. - Java8 also declared the Comparator interface as a FunctionalInterface (annotating with
@FunctionalInterface
), thus lambdas and method references can also be used now as implementation of thecompare()
method.
Apart from the SAM (single abstract method) compare()
and equals()
method, Comparator now contains several default and static methods:
Example: Comparator enhancements in Java8
click to expand/collapse
/*
* Example demonstrating the enhancements in Comparator interface.
*/
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class ComparatorEnhancementsExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(3, 1, 4, 2, 5);
// naturalOrder()
numbers.sort(Comparator.naturalOrder());
System.out.println("Soring numbers in natural order: ");
System.out.println(numbers);
System.out.println();
// reverseOrder()
numbers.sort(Comparator.reverseOrder());
System.out.println("Soring numbers in reverse natural order: ");
System.out.println(numbers);
System.out.println();
// comparing()
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
fruits.sort(Comparator.comparing(String::length));
System.out.println("Sorting fruits by their name length:");
System.out.println(fruits);
System.out.println();
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Alice",30),
new Person("Bob", 30),
new Person("Charlie", 18),
new Person("Charlie", 20)
);
// comparingInt() method
people.sort(Comparator.comparingInt(Person::getAge));
System.out.println("Sorting people by age:");
System.out.println(people);
System.out.println();
// reversed()
Comparator<Person> reversedComparator = Comparator.comparingInt(Person::getAge).reversed();
people.sort(reversedComparator);
System.out.println("Sorting people by age in reverse order:");
System.out.println(people);
System.out.println();
// thenComparing() method
Comparator<Person> ageComparator = Comparator.comparingInt(Person::getAge);
Comparator<Person> nameComparator = Comparator.comparing(Person::getName);
Comparator<Person> combinedComparator = ageComparator.thenComparing(nameComparator);
people.sort(combinedComparator);
System.out.println("Sorting people by age, then by name:");
System.out.println(people);
System.out.println();
// thenComparing() method with key extractor
Comparator<Person> combinedComparatorWithKeyExtractor = Comparator.comparing(Person::getName)
.thenComparing(Person::getAge);
people.sort(combinedComparatorWithKeyExtractor);
System.out.println("Sorting people by name, then by age:");
System.out.println(people);
System.out.println();
List<String> names = Arrays.asList("Alice", "Bob", null, "Charlie", null);
// nullsFirst() method
names.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
System.out.println("Sorting names with null values first:");
System.out.println(names);
System.out.println();
// nullsLast() method
names.sort(Comparator.nullsLast(Comparator.naturalOrder()));
System.out.println("Sorting names with null values last:");
System.out.println(names);
System.out.println();
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Output:
click to expand/collapse
Soring numbers in natural order:
[1, 2, 3, 4, 5]
Soring numbers in reverse natural order:
[5, 4, 3, 2, 1]
Sorting fruits by their name length:
[Apple, Banana, Orange]
Sorting people by age:
[{name='Charlie', age=18}, {name='Charlie', age=20}, {name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}]
Sorting people by age in reverse order:
[{name='Alice', age=30}, {name='Bob', age=30}, {name='Alice', age=25}, {name='Charlie', age=20}, {name='Charlie', age=18}]
Sorting people by age, then by name:
[{name='Charlie', age=18}, {name='Charlie', age=20}, {name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}]
Sorting people by name, then by age:
[{name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}, {name='Charlie', age=18}, {name='Charlie', age=20}]
Sorting names with null values first:
[null, null, Alice, Bob, Charlie]
Sorting names with null values last:
[Alice, Bob, Charlie, null, null]
static <T extends Comparable<? super T>> Comparator<T> naturalOrder()
- Returns a comparator that sorts the elements in their natural order Example:
List<Integer> numbers = Arrays.asList(3, 1, 4, 2, 5);
//naturalOrder()
numbers.sort(Comparator.naturalOrder()); // Sort in ascending order
System.out.println("Soring numbers in natural order: ");
System.out.println(numbers);
Output:
Soring numbers in natural order:
[1, 2, 3, 4, 5]
static <T extends Comparable<? super T>> Comparator<T> reverseOrder()
- Returns a comparator that sorts the elements in their reverse natural order. Example:
List<Integer> numbers = Arrays.asList(3, 1, 4, 2, 5);
//reverseOrder()
numbers.sort(Comparator.reverseOrder()); // Sort in descending order
System.out.println("Soring numbers in reverse natural order: ");
System.out.println(numbers);
Output:
Soring numbers in reverse natural order:
[5, 4, 3, 2, 1]
static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? superT, ? extends U> keyExtractor)
- Accepts a keyExtractor Function (functional interface), that specifies the sort keys and returns a Comparator that sorts based on the natural ordering of specified key.
- The sorting will be done on keys returned by teh keyExtractor function.
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
//comparing()
fruits.sort(Comparator.comparing(String::length));
System.out.println("Sorting fruits by their name length:");
System.out.println(fruits);
Output:
Sorting fruits by their name length:
[Apple, Banana, Orange]
static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor)
static <T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor)
- Similar to comparing() method
- Specialized comparing() method for comparing objects based on natural ordering of int,long and double keys (returned by the keyExtractor function) respectively.
List<Person> people=Arrays.asList(
new Person("Alice",25),
new Person("Alice",30),
new Person("Bob",30),
new Person("Charlie",18),
new Person("Charlie",20));
//comparingInt()
people.sort(Comparator.comparingInt(Person::getAge));
System.out.println("Sorting people by age:");
System.out.println(people);
Output:
Sorting people by age:
[{name='Charlie', age=18}, {name='Charlie', age=20}, {name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}]
default Comparator<T> thenComparing(Comparator<? super T> other)
- Many times there is need to sort with multiple sort orders. i.e.: on more than one attributes of an object.
- The second level sort order gets used if the first level sort criteria is indecisive.
- Chains multiple comparators to establish the secondary sort criteria.
List<Person> people=Arrays.asList(
new Person("Alice",25),
new Person("Alice",30),
new Person("Bob",30),
new Person("Charlie",18),
new Person("Charlie",20));
Comparator<Person> ageComparator=Comparator.comparingInt(Person::getAge);
Comparator<Person> nameComparator=Comparator.comparing(Person::getName);
// thenComparing()
Comparator<Person> combinedComparator=ageComparator.thenComparing(nameComparator);
people.sort(combinedComparator);
System.out.println("Sorting people by age, then by name:");
System.out.println(people);
Output:
Sorting people by age, then by name:
[{name='Charlie', age=18}, {name='Charlie', age=20}, {name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}]
default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor)
- Similar to thenComparing()
- Performs a secondary sort based on the extracted key's natural order.
List<Person> people=Arrays.asList(
new Person("Alice",25),
new Person("Alice",30),
new Person("Bob",30),
new Person("Charlie",18),
new Person("Charlie",20));
// thenComparing() method with key extractor
Comparator<Person> combinedComparatorWithKeyExtractor = Comparator.comparing(Person::getName)
.thenComparing(Person::getAge);
people.sort(combinedComparatorWithKeyExtractor);
System.out.println("Sorting people by name, then by age:");
System.out.println(people);
Output:
Sorting people by name, then by age:
[{name='Alice', age=25}, {name='Alice', age=30}, {name='Bob', age=30}, {name='Charlie', age=18}, {name='Charlie', age=20}]
thenComparing methods also have overloaded versions for int, long, double as thenComparingInt(), thenComparingLong(), thenComparingDouble()
default Comparator<T> nullsFirst()
- used to handle null values while sorting.
- Returns a comparator that considers null values lower than non-null values (as per the compare method).
- so, the null values will come before the non-null values
List<String> names = Arrays.asList("Alice", "Bob", null, "Charlie", null);
// nullsFirst() method
names.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
System.out.println("Sorting names with null values first:");
System.out.println(names);
Output:
Sorting names with null values first:
[null, null, Alice, Bob, Charlie]
default Comparator<T> nullsLast()
- used to handle null values while sorting.
- Returns a comparator that considers null values higher than non-null values (as per the compare method).
- so, the null values will come after the non-null values
List<String> names = Arrays.asList("Alice", "Bob", null, "Charlie", null);
// nullsLast() method
names.sort(Comparator.nullsLast(Comparator.naturalOrder()));
System.out.println("Sorting names with null values last:");
System.out.println(names);
System.out.println();
Output:
Sorting names with null values last:
[Alice, Bob, Charlie, null, null]
default Comparator<T> reversed()
- Returns a comparator that sorts the elements in the reverse order of the comparator on which it was invoked. Example:
List<Person> people=Arrays.asList(
new Person("Alice",25),
new Person("Alice",30),
new Person("Bob",30),
new Person("Charlie",18),
new Person("Charlie",20));
// reversed()
Comparator<Person> reversedComparator = Comparator.comparingInt(Person::getAge).reversed();
people.sort(reversedComparator);
System.out.println("Sorting people by age in reverse order:");
System.out.println(people);
Output:
Sorting people by age in reverse order:
[{name='Alice', age=30}, {name='Bob', age=30}, {name='Alice', age=25}, {name='Charlie', age=20}, {name='Charlie', age=18}]