← Back to Advanced Java Course | Chapter 5: Collections Advanced | Lesson 3 of 4

Java Comparable & Comparator

Natural Sorting with Comparable

You can define a default, natural sort order for your own custom classes by implementing the Comparable interface and overriding its compareTo method. Collections like TreeSet and TreeMap will automatically use this natural ordering unless you explicitly supply something else.

Example: Natural Sorting with Comparable

java
import java.util.*;
public class Main {
	static class Person implements Comparable<Person> {
		String name; int age;
		Person(String name, int age) { this.name = name; this.age = age; }
		public int compareTo(Person other) { return Integer.compare(age, other.age); }
		public String toString() { return name + "(" + age + ")"; }
	}
	public static void main(String[] args) {
		List<Person> people = new ArrayList<>(List.of(new Person("Bo", 30), new Person("Al", 20)));
		Collections.sort(people);
		System.out.println(people);
	}
}

Custom Sorting with Comparator

If you want to sort the same objects in several different ways without touching their class definition, you can define a separate class implementing the Comparator interface for each ordering you need. This decouples the sorting logic entirely from the object being sorted.

Example: Custom Sorting with Comparator

java
import java.util.*;
public class Main {
	static class ByName implements Comparator<String> {
		public int compare(String a, String b) { return a.compareTo(b); }
	}
	public static void main(String[] args) {
		List<String> names = new ArrayList<>(List.of("Zoe", "Amy", "Max"));
		names.sort(new ByName());
		System.out.println(names);
	}
}

Comparator Lambda Syntax

You can write ad-hoc comparators quickly using lambda expressions instead of declaring a whole named class purely for one comparison. This is the most common way comparators are written in modern Java code, especially for one-off sorts.

Example: Comparator Lambda Syntax

java
import java.util.*;
public class Main {
	public static void main(String[] args) {
		List<String> names = new ArrayList<>(List.of("Zoe", "Amy", "Max"));
		names.sort((a, b) -> a.compareTo(b));
		System.out.println(names);
	}
}

Chaining Comparators

You can chain several comparators together using the thenComparing() method to define fallback sorting rules, so that when two elements tie on the primary comparison, the next comparator in the chain breaks the tie, and so on down the chain.

Example: Chaining Comparators

java
import java.util.*;
public class Main {
	record Person(String name, int age) {}
	public static void main(String[] args) {
		List<Person> people = new ArrayList<>(List.of(new Person("Amy", 30), new Person("Amy", 20)));
		people.sort(Comparator.comparing(Person::name).thenComparing(Person::age));
		System.out.println(people);
	}
}

Null-Safe Comparators

Sorting a collection that contains null elements with a custom comparator can throw a NullPointerException the moment the comparator tries to compare against one. You can guard against this by wrapping your comparator with Comparator.nullsFirst() or nullsLast(), which push nulls to whichever end of the ordering you choose.

Example: Null-Safe Comparators

java
import java.util.*;
public class Main {
	public static void main(String[] args) {
		List<String> names = new ArrayList<>(Arrays.asList("Zoe", null, "Amy"));
		names.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
		System.out.println(names);
	}
}
🔒

Chapter Quiz — Complete all 4 topics to unlock

0/4 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.