Card 05/ 09

ExampleDifficulty: Intermediate2 min

Strategy in the Wild: Comparator in Java and sorted(key=) in Python

The sort is written once; the order is the strategy you pass it.

The sort functions of Java and Python are both contexts that take a strategy. The sorting algorithm is written once inside the library, and the caller passes in the part that varies: how two elements are ordered. This card sorts the same three books by title and then newest first in each language, and names the participant each piece plays.

The same books sorted two ways

java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class StComparator {
    record Book(String title, int year) {}

    public static void main(String[] args) {
        List<Book> books = new ArrayList<>(List.of(
                new Book("Refactoring", 1999),
                new Book("Design Patterns", 1994),
                new Book("Clean Code", 2008)));

        books.sort(Comparator.comparing(Book::title));
        System.out.println("by title: " + books.stream().map(Book::title).toList());

        books.sort(Comparator.comparingInt(Book::year).reversed());
        System.out.println("newest first: " + books.stream().map(Book::title).toList());
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
by title: [Clean Code, Design Patterns, Refactoring]
newest first: [Clean Code, Refactoring, Design Patterns]
`Comparator.comparing` builds a strategy from a method reference.

List.sort is the context. A Comparator is the strategy interface, with one method, compare(a, b), and each call to Comparator.comparing returns a concrete strategy. reversed() wraps one strategy in another that inverts it.

py
from typing import NamedTuple


class Book(NamedTuple):
    title: str
    year: int


books = [Book("Refactoring", 1999), Book("Design Patterns", 1994), Book("Clean Code", 2008)]

by_title = sorted(books, key=lambda book: book.title)
print("by title:", [book.title for book in by_title])

newest_first = sorted(books, key=lambda book: book.year, reverse=True)
print("newest first:", [book.title for book in newest_first])
OutputRan with python:3.12-slim (Python 3.12.13)
by title: ['Clean Code', 'Design Patterns', 'Refactoring']
newest first: ['Clean Code', 'Refactoring', 'Design Patterns']
The `key` argument is a function that turns each book into the value to sort by.

sorted is the context, and the function passed as key is the strategy. Python asks for a key rather than a comparison: it calls the function once per element and sorts by the results, which is simpler to write and faster than comparing pairs.

The two outputs agree, and so do the shapes. Neither library has a branch for titles or years; each was written long before these books existed, and orders them correctly because the caller supplied the one piece of knowledge it lacked.

What differs is the form of the strategy. Java needs an object that implements an interface, though a method reference such as Book::title lets the compiler build it. Python takes any function. In both, the library is closed to change and open to any order a caller can describe.