Master Java’s Comparator: Sorting Collections, Streams, and Sets Made Easy
This article explains Java's Comparator interface, its compare method, lambda definitions, and how to use it with Collections.sort, List.sort, Arrays.sort, Stream.sorted, as well as with SortedSet and SortedMap structures, covering static and default methods like reversed, nullsFirst, thenComparing, and more.
Java's java.util.Comparator functional interface imposes a total ordering on object collections.
Comparator Functional Method
The core method is int compare(T o1, T o2), which returns a negative integer, zero, or a positive integer when the first argument is less than, equal to, or greater than the second argument. The ordering must be consistent with equals —i.e., c.compare(e1, e2) == 0 should have the same boolean result as e1.equals(e2).
Comparator Method Signature
int compare(T o1, T o2)Defining Comparators with Lambda Expressions
Lambda expressions can be used to create comparator instances concisely:
Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());Using Comparator with Sorting APIs
The comparator can be passed to various sorting methods:
Stream.sorted(comparator) Collections.sort(list, comparator) List.sort(comparator) Arrays.sort(array, comparator)Example with List.sort:
List<Student> list = Student.getStudentList();
Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
list.sort(ageComp);
list.forEach(System.out::println);Comparator with Stream.sorted
list.stream()
.sorted(ageComp)
.forEach(System.out::println);Comparator with Collections.sort
Collections.sort(list, ageComp);
list.forEach(System.out::println);Comparator with Arrays.sort
Student[] array = {s1, s2, s3};
Arrays.sort(array, ageComp);
for (Student s : array) {
System.out.println(s);
}Static and Default Methods (Java 8+)
Comparator provides several useful static and default methods: default Comparator<T> reversed() – returns a comparator that imposes the reverse ordering. static <T> Comparator<T> reverseOrder() – returns a comparator that imposes the reverse of the natural order. static <T> Comparator<T> naturalOrder() – returns a comparator that imposes the natural order.
static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)– treats null as less than non‑null.
static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)– treats null as greater than non‑null.
static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)– creates a comparator based on a key extractor.
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) default Comparator<T> thenComparing(Comparator<? super T> other) default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T,? extends U> keyExtractor) default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor)Examples of Static Methods
reversed :
Comparator<Student> nameComparator = (s1, s2) -> s1.getName().compareTo(s2.getName());
Collections.sort(list, nameComparator.reversed());nullsFirst :
List<Student> list = Arrays.asList(s1, s2, null, s3);
Collections.sort(list, Comparator.nullsFirst(Comparator.comparing(Student::getName)));comparingInt (sorting by age):
Collections.sort(list, Comparator.comparingInt(Student::getAge));comparingLong (sorting by home distance):
Collections.sort(list, Comparator.comparingLong(Student::getHomeDistance));comparingDouble (sorting by weight):
Collections.sort(list, Comparator.comparingDouble(Student::getWeight));Chaining Comparators with thenComparing
Example: first sort by age, then by name:
Comparator<Student> comp = Comparator.comparing(Student::getAge)
.thenComparing(Student::getName);
Collections.sort(list, comp);More complex chaining can involve multiple keys and custom comparators.
Comparator with SortedSet
TreeSet and ConcurrentSkipListSet can be constructed with a comparator to control element order:
Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
TreeSet<Student> treeSet = new TreeSet<>(ageComparator);
treeSet.addAll(Arrays.asList(s1, s2, s3));When no comparator is supplied, the set relies on the elements' natural ordering (i.e., they must implement Comparable).
Comparator with SortedMap
TreeMap and ConcurrentSkipListMap accept a comparator to order their keys:
Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);
TreeMap<Student, String> treeMap = new TreeMap<>(ageComparator);
treeMap.put(s1, "Varanasi");Without a comparator, the map orders keys according to their natural order.
Conclusion
The Comparator interface is a versatile tool for defining custom ordering in Java. By leveraging lambda expressions, static factory methods, and default chaining methods, developers can sort collections, streams, sets, and maps in a concise and expressive manner.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
