0Pricing
Java Academy · Lekcja

Sortowanie według wielu kluczy za pomocą thenComparing

Łącz komparatory z thenComparing, aby sortować według wielu pól zgodnie z priorytetami.

Sortowanie według wielu kluczy za pomocą thenComparing to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Sortowanie według wielu kluczy

thenComparing() łączy komparatory, aby sortować według wielu pól. Najpierw odbywa się sortowanie główne, następnie — w przypadku równości — pomocnicze itd.

Podstawowe użycie thenComparing

thenComparing() służy do rozstrzygania remisów po zastosowaniu głównego komparatora.

import java.util.*;

record Student(String name, int grade, double gpa) {}

List<Student> students = List.of(
    new Student("Alice",   10, 3.9),
    new Student("Bob",     11, 3.7),
    new Student("Charlie", 10, 3.8),
    new Student("Diana",   11, 3.7)
);

Comparator<Student> sort = Comparator.comparingInt(Student::grade)
    .thenComparingDouble(Student::gpa).reversed()
    .thenComparing(Student::name);

students.stream().sorted(sort).forEach(System.out::println);

Sortowanie trzypoziomowe

Można łączyć dowolną liczbę poziomów thenComparing.

record Employee(String dept, String team, String name, double salary) {}

List<Employee> employees = List.of(
    new Employee("Eng", "Backend", "Alice",   95000),
    new Employee("Eng", "Backend", "Bob",     92000),
    new Employee("Eng", "Frontend","Charlie", 88000),
    new Employee("Mkt", "Growth",  "Diana",   75000)
);

Comparator<Employee> sort = Comparator
    .comparing(Employee::dept)
    .thenComparing(Employee::team)
    .thenComparing(Comparator.comparingDouble(Employee::salary).reversed());

employees.stream().sorted(sort)
    .forEach(e -> System.out.printf("%s > %s > %s: $%.0f%n",
        e.dept(), e.team(), e.name(), e.salary()));

thenComparing z Comparator

Gdy dla kluczy pomocniczych potrzebny jest odwrócony lub niestandardowy porządek, należy użyć przeciążenia przyjmującego Comparator.

Comparator<Employee> sort = Comparator
    .comparing(Employee::dept)
    .thenComparing(Comparator.comparing(Employee::salary).reversed()); // highest paid first
    // equivalent: .thenComparingDouble(Employee::salary, Comparator.reverseOrder())

Sortowanie z polami null

Połączenie thenComparing z nullsLast/nullsFirst pozwala poprawnie obsługiwać opcjonalne pola.

record Product(String name, String category, Double rating) {}

Comparator<Product> sort = Comparator
    .comparing(Product::category, Comparator.nullsLast(Comparator.naturalOrder()))
    .thenComparing(Product::rating,  Comparator.nullsLast(Comparator.reverseOrder()))
    .thenComparing(Product::name);

Dynamiczny wybór klucza sortowania

Klucz sortowania można wybrać w czasie działania programu na podstawie preferencji użytkownika.

static Comparator<Product> sortBy(String key) {
    return switch (key) {
        case "price"    -> Comparator.comparingDouble(Product::price);
        case "name"     -> Comparator.comparing(Product::name);
        case "stock"    -> Comparator.comparingInt(Product::stock).reversed();
        default         -> Comparator.comparingDouble(Product::price);
    };
}

String userChoice = "stock";
products.stream().sorted(sortBy(userChoice)).forEach(System.out::println);

thenComparing z funkcją ekstrakcji

Przeciążenie thenComparing(Function<T,U>) wyodrębnia klucz Comparable na potrzeby sortowania pomocniczego, bez konieczności jawnego używania Comparator.

record Event(String type, int year, int month, int day) {}

Comparator<Event> chronological = Comparator
    .comparing(Event::type)
    .thenComparingInt(Event::year)
    .thenComparingInt(Event::month)
    .thenComparingInt(Event::day);

Sortowanie kolekcji rekordów

Rekordy z metodami dostępowymi bezproblemowo współpracują z odwołaniami do metod w łańcuchach komparatorów.

record Order(String customerId, String status, double total, java.time.LocalDate date) {}

List<Order> orders = loadOrders();

Comparator<Order> byCustomerThenDate = Comparator
    .comparing(Order::customerId)
    .thenComparing(Order::date, Comparator.reverseOrder()); // newest first per customer

orders.stream().sorted(byCustomerThenDate)
    .collect(Collectors.groupingBy(Order::customerId))
    .forEach((cust, ords) -> {
        System.out.println("Customer: " + cust);
        ords.forEach(o -> System.out.println("  " + o.date() + " $" + o.total()));
    });

Gwarancja stabilnego sortowania

Metody List.sort() i Arrays.sort() dla obiektów w Javie używają stabilnego sortowania przez scalanie. Elementy uznane za równe podczas porównywania zachowują pierwotną kolejność względną.

// Stable sort example:
// First sort by name, then by dept
// Equal dept elements preserve the name order from previous sort
List<Employee> sorted = employees.stream()
    .sorted(Comparator.comparing(Employee::name))     // 1st pass
    .sorted(Comparator.comparing(Employee::dept))     // 2nd pass (stable!)
    .collect(Collectors.toList());
// Within each dept, employees remain alphabetically sorted

Comparator.comparing z komparatorem klucza

Dwuargumentowa forma Comparator.comparing(keyExtractor, keyComparator) pozwala określić zarówno klucz, jak i jego porządek.

// Sort by name length, then alphabetically within same length
Comparator<String> c = Comparator
    .comparing(String::length, Integer::compare)
    .thenComparing(Comparator.naturalOrder());

List<String> words = new ArrayList<>(List.of("fig","apple","kiwi","banana","pea"));
words.sort(c);
System.out.println(words); // [fig, pea, kiwi, apple, banana]

Przykład z życia: wyniki wyszukiwania lotów

Sortowanie wyników wyszukiwania lotów według ceny, następnie czasu trwania, a na końcu godziny wylotu.

record Flight(String airline, double price, int durationMin, java.time.LocalTime departs) {}

Comparator<Flight> bestFlight = Comparator
    .comparingDouble(Flight::price)
    .thenComparingInt(Flight::durationMin)
    .thenComparing(Flight::departs);

Zbieranie posortowanych wyników

Połącz sortowanie według wielu kluczy z kolektorami, aby tworzyć posortowane grupy lub mapy.

Map<String, List<Employee>> byDept = employees.stream()
    .sorted(Comparator.comparing(Employee::dept)
        .thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
    .collect(Collectors.groupingBy(Employee::dept,
        LinkedHashMap::new,  // preserve insertion (sorted) order
        Collectors.toList()));

byDept.forEach((dept, emps) -> {
    System.out.println(dept + ":");
    emps.forEach(e -> System.out.println("  " + e.name() + " $" + e.salary()));
});

Szybki test

Sortowanie ma odbywać się najpierw według działu alfabetycznie, a następnie według wynagrodzenia malejąco. Który łańcuch jest poprawny?

Podsumowanie: sortowanie wielokryterialne z thenComparing

Najważniejsze informacje:

  • thenComparing() dodaje pomocnicze klucze sortowania, stosowane, gdy główne klucze są równe
  • Można łączyć dowolną liczbę poziomów thenComparing
  • Użyj thenComparing(Comparator) wraz z reversed(), gdy pomocniczy klucz wymaga kolejności malejącej
  • Połącz z nullsFirst/nullsLast dla opcjonalnych pól
  • Metoda List.sort() w Javie jest stabilna — elementy równe zachowują pierwotną kolejność
  • Dynamiczny wybór sortowania: użyj switch, aby w czasie działania zwrócić właściwy Comparator

Często zadawane pytania

Czy lekcja „Sortowanie według wielu kluczy za pomocą thenComparing” jest bezpłatna?

Tak — pełny tekst „Sortowanie według wielu kluczy za pomocą thenComparing” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Sortowanie według wielu kluczy za pomocą thenComparing”?

Łącz komparatory z thenComparing, aby sortować według wielu pól zgodnie z priorytetami. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Sortowanie według wielu kluczy za pomocą thenComparing”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Interfejs Comparable
  2. Comparator i sortowanie za pomocą lambd
  3. Sortowanie według wielu kluczy za pomocą thenComparing
  4. Sortowanie tablic i kolekcji w praktyce
← Powrót do Java Academy