Sorting & Comparators
Sorting is one of the most common operations on collections: products by price, people by surname then first name, tasks by priority. Java sorts using either a class's natural order (Comparable) or a separate Comparator, and modern comparator builders make even complex orderings one readable line.
Natural order
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = new ArrayList<>(List.of("mei", "Ana", "ben", "Lucas")); names.sort(null); System.out.println(names); List<Integer> nums = new ArrayList<>(List.of(42, 7, 19)); nums.sort(null); System.out.println(nums); } }
[Ana, Lucas, ben, mei] [7, 19, 42]
Strings, numbers and dates already implement Comparable. Passing null (or calling Collections.sort) uses that natural order. Note that string order is by character code, so capital letters come before lowercase ones.
Implementing Comparable
import java.util.ArrayList; import java.util.Collections; import java.util.List; record Version(int major, int minor) implements Comparable<Version> { @Override public int compareTo(Version other) { if (major != other.major) return Integer.compare(major, other.major); return Integer.compare(minor, other.minor); } @Override public String toString() { return major + "." + minor; } } public class Main { public static void main(String[] args) { List<Version> versions = new ArrayList<>(List.of(new Version(2, 10), new Version(2, 9), new Version(1, 4))); Collections.sort(versions); System.out.println(versions); } }
[1.4, 2.9, 2.10]
compareTo returns a negative number if this object comes first, zero if equal, positive if it comes after. Use Integer.compare rather than subtracting, which can overflow. As text, "2.10" would sort before "2.9" — this is why comparing parsed values matters.
Comparators
import java.util.ArrayList; import java.util.Comparator; import java.util.List; record Product(String name, String category, double price) {} public class Main { public static void main(String[] args) { List<Product> products = new ArrayList<>(List.of( new Product("Mug", "Home", 12.5), new Product("Lamp", "Home", 34.0), new Product("Earbuds", "Electronics", 59.99), new Product("Charger", "Electronics", 19.99) )); products.sort(Comparator.comparing(Product::price)); System.out.println(products.stream().map(Product::name).toList()); products.sort(Comparator.comparing(Product::price).reversed()); System.out.println(products.stream().map(Product::name).toList()); products.sort(Comparator.comparing(Product::category).thenComparing(Product::name)); System.out.println(products.stream().map(p -> p.category() + "/" + p.name()).toList()); } }
[Mug, Charger, Lamp, Earbuds] [Earbuds, Lamp, Charger, Mug] [Electronics/Charger, Electronics/Earbuds, Home/Lamp, Home/Mug]
A Comparator defines an ordering outside the class, so one class can be sorted many ways. Comparator.comparing(key) builds one from a key; reversed() and thenComparing combine them. (Product::price is a method reference and .stream()…toList() is a stream — both explained in lessons 45–46.)
Nulls and case
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; public class Main { public static void main(String[] args) { List<String> cities = new ArrayList<>(Arrays.asList("lisbon", null, "Berlin", "dublin")); cities.sort(Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER)); System.out.println(cities); } }
[Berlin, dublin, lisbon, null]
nullsFirst/nullsLast wrap a comparator to handle null safely, and String.CASE_INSENSITIVE_ORDER ignores case. For human-language sorting with accents, use java.text.Collator.
Sorting arrays and sorted collections
import java.util.Arrays; import java.util.Comparator; import java.util.TreeMap; public class Main { public static void main(String[] args) { String[] words = {"banana", "kiwi", "apple", "fig"}; Arrays.sort(words, Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())); System.out.println(Arrays.toString(words)); TreeMap<String, Integer> byLength = new TreeMap<>(Comparator.reverseOrder()); byLength.put("a", 1); byLength.put("c", 3); byLength.put("b", 2); System.out.println(byLength); } }
[fig, kiwi, apple, banana]
{c=3, b=2, a=1}Java's sorts are stable: equal elements keep their relative order, so you can sort by a secondary key first and then by the primary key.
Try it yourself
Rank employees by salary (highest first), breaking ties by name.
1. Mei 98000 2. Jonas 85000 3. Amara 68000 4. Tomás 68000