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

Java Comparable & Comparator

Comparable and Comparator tell Java how to put objects in order, like rules for lining up kids by height or by name. Comparable is the natural rule, and Comparator is a rule you choose.
Syntax
java
class ClassName implements Comparable<ClassName> {
    public int compareTo(ClassName other) {
        return Integer.compare(this.field, other.field);
    }
}

Comparator<ClassName> comparator = Comparator.comparing(ClassName::getField);

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.* so it can be used by its short name
import java.util.*;
// Define the class Main
public class Main {
	// Define the class Person
	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 + ")"; }
	}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a new ArrayList object and store it in people
		List<Person> people = new ArrayList<>(List.of(new Person("Bo", 30), new Person("Al", 20)));
		Collections.sort(people);
		// Print a line to the console
		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.* so it can be used by its short name
import java.util.*;
// Define the class Main
public class Main {
	// Define the class ByName
	static class ByName implements Comparator<String> {
		public int compare(String a, String b) { return a.compareTo(b); }
	}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a new ArrayList object and store it in names
		List<String> names = new ArrayList<>(List.of("Zoe", "Amy", "Max"));
		names.sort(new ByName());
		// Print a line to the console
		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.* so it can be used by its short name
import java.util.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a new ArrayList object and store it in names
		List<String> names = new ArrayList<>(List.of("Zoe", "Amy", "Max"));
		names.sort((a, b) -> a.compareTo(b));
		// Print a line to the console
		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.* so it can be used by its short name
import java.util.*;
// Define the class Main
public class Main {
	// Define the record Person (an immutable data carrier)
	record Person(String name, int age) {}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a new ArrayList object and store it in people
		List<Person> people = new ArrayList<>(List.of(new Person("Amy", 30), new Person("Amy", 20)));
		people.sort(Comparator.comparing(Person::name).thenComparing(Person::age));
		// Print a line to the console
		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.* so it can be used by its short name
import java.util.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a new ArrayList object and store it in names
		List<String> names = new ArrayList<>(Arrays.asList("Zoe", null, "Amy"));
		names.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
		// Print a line to the console
		System.out.println(names);
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Calling Collections.sort(list) on a class that does not implement Comparable, which does not compile; implement Comparable or pass a Comparator.
  2. Writing a comparator as a.age - b.age, which can overflow for large values; use Integer.compare(a.age, b.age).
  3. Sorting a list that contains null values with a plain comparator, which throws a NullPointerException; wrap it with Comparator.nullsFirst.
🔒

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.