Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Sorting and comparators

Lists often need ordering: names alphabetically, products by price, employees by department and salary. Java can sort by itself — you only have to explain the order.

Natural order

Numbers and strings already know how to compare with each other. This is called natural order. The Collections.sort() method sorts a list by it:

void main() {
    List<Integer> numbers = new ArrayList<>(List.of(5, 2, 9, 1));
    Collections.sort(numbers);
    IO.println(numbers);
}

Output:

[1, 2, 5, 9]

Strings are sorted by character codes: capital letters come before lowercase ones.

A comparator and reverse order

A comparator (Comparator) is an object that compares two elements and thereby defines an order. The list's sort() method accepts a comparator. The ready-made Comparator.reverseOrder() sorts in descending order:

void main() {
    List<String> names = new ArrayList<>(List.of("Oleg", "Anna", "Ira"));
    names.sort(Comparator.reverseOrder());
    IO.println(names);
}

Output:

[Oleg, Ira, Anna]

Comparator.comparing()

For your own objects a comparator is conveniently built with Comparator.comparing(): you tell it which value to compare by. The notation Product::price is a method reference, a short way to say "take price() from each product":

record Product(String title, double price) {}
 
void main() {
    List<Product> products = new ArrayList<>(List.of(
            new Product("Tea", 4.5),
            new Product("Coffee", 7.0),
            new Product("Juice", 3.2)));
 
    products.sort(Comparator.comparing(Product::price));
    for (Product p : products) {
        IO.println(p.title() + " " + p.price());
    }
}

Output:

Juice 3.2
Tea 4.5
Coffee 7.0

Add .reversed() and the order flips: Comparator.comparing(Product::price).reversed().

Several fields

If the values of the first field are equal, the second field is given with the thenComparing() method. Here employees are sorted by department, and within a department by name:

record Employee(String name, String department) {}
 
void main() {
    List<Employee> staff = new ArrayList<>(List.of(
            new Employee("Oleg", "warehouse"),
            new Employee("Anna", "sales"),
            new Employee("Ira", "warehouse"),
            new Employee("Boris", "sales")));
 
    staff.sort(Comparator.comparing(Employee::department).thenComparing(Employee::name));
    staff.forEach(e -> IO.println(e.department() + ": " + e.name()));
}

Output:

sales: Anna
sales: Boris
warehouse: Ira
warehouse: Oleg

Your own natural order: Comparable

A class can define its own natural order by implementing the Comparable interface and its compareTo() method. The method returns a negative number if the current object is smaller than the other, zero if they are equal, and a positive number if it is greater:

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);
    }
}
 
void main() {
    List<Version> versions = new ArrayList<>(List.of(
            new Version(2, 1), new Version(1, 9), new Version(2, 0)));
    Collections.sort(versions);
    IO.println(versions);
}

Output:

[Version[major=1, minor=9], Version[major=2, minor=0], Version[major=2, minor=1]]

Common mistake

Sorting changes the list itself. An unmodifiable list from List.of() cannot be sorted:

void main() {
    List<Integer> numbers = List.of(3, 1, 2);
    Collections.sort(numbers);
}

Output:

Exception in thread "main" java.lang.UnsupportedOperationException

Make a modifiable copy first — List<Integer> sorted = new ArrayList<>(numbers); — and sort that.

Thanks for reading!

Donate to the site — we work hard for you.