0Pricing
Java Academy · Lekcja

Własny porządek w kolekcjach typu Tree

Przekazuj Comparator do TreeMap/TreeSet, aby definiować porządek sortowania właściwy dla danej domeny.

Własny porządek w kolekcjach typu Tree to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 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.

Dlaczego własne sortowanie?

Kolekcje oparte na drzewach (TreeMap, TreeSet) domyślnie używają kolejności naturalnej. Gdy obiekty domenowe nie mają naturalnego porządku lub potrzebujesz innego sortowania, przekaż obiekt Comparator podczas tworzenia kolekcji.

Comparator podczas tworzenia TreeMap

Przekaż komparator, aby sortować klucze według własnej reguły — na przykład w odwrotnej kolejności alfabetycznej:

import java.util.*;

TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
map.put("banana", 2);
map.put("apple", 1);
map.put("cherry", 3);

map.forEach((k,v) -> System.out.println(k)); // cherry, banana, apple

Comparator podczas tworzenia TreeSet

Przekaż komparator do TreeSet, aby niezależnie od kolejności naturalnej elementów określić kolejność sortowania:

TreeSet<String> byLength = new TreeSet<>(
    Comparator.comparingInt(String::length)
              .thenComparing(Comparator.naturalOrder())
);
byLength.addAll(List.of("fig","apple","kiwi","date","banana"));

for (String s : byLength) System.out.print(s + " ");
// fig date kiwi apple banana

Sortowanie obiektów według wielu pól

Łącz komparatory, aby sortować według pola głównego, a następnie według pola dodatkowego rozstrzygającego remisy:

record Employee(String name, String dept, int salary) {}

TreeSet<Employee> sorted = new TreeSet<>(
    Comparator.comparing(Employee::dept)
              .thenComparingInt(Employee::salary).reversed()
              .thenComparing(Employee::name)
);
sorted.add(new Employee("Alice", "Eng", 90_000));
sorted.add(new Employee("Bob",   "Eng", 85_000));
sorted.add(new Employee("Carol", "HR",  70_000));

for (Employee e : sorted) System.out.println(e.dept()+" "+e.name());

Zgodność z equals

Kluczowa zasada: komparator używany przez TreeSet/TreeMap definiuje równość na potrzeby kolekcji. Jeśli comparator.compare(a, b) == 0, zarówno a, jak i b są uznawane za ten sam klucz — nawet jeśli a.equals(b) zwraca false.

// Comparator ignoring case — "apple" and "APPLE" become the same key!
TreeSet<String> ci = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
ci.add("apple");
ci.add("APPLE"); // not added — same by comparator
System.out.println(ci.size()); // 1

TreeMap bez rozróżniania wielkości liter

Częsta potrzeba praktyczna: TreeMap, w której kluczami są napisy bez rozróżniania wielkości liter (przydatna w przypadku nagłówków HTTP i kluczy konfiguracji):

TreeMap<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
headers.put("Content-Type", "application/json");
headers.put("content-type", "text/html"); // overwrites!

System.out.println(headers.get("CONTENT-TYPE")); // text/html
System.out.println(headers.size()); // 1

Komparator bezpieczny dla null

Kolekcje oparte na drzewach z kolejnością naturalną zgłaszają NPE dla kluczy null. Użyj komparatora bezpiecznego dla null, aby zezwolić na null jako najmniejszy klucz:

TreeMap<String, Integer> map = new TreeMap<>(
    Comparator.nullsFirst(Comparator.naturalOrder())
);
map.put(null, 0);
map.put("b", 2);
map.put("a", 1);

map.forEach((k,v) -> System.out.println(k + "=" + v));
// null=0, a=1, b=2

Przykład obiektu domenowego: produkt według ceny

Sortuj produkty według ceny w TreeSet, używając nazwy jako kryterium rozstrzygającego remis, aby zachować unikalność:

record Product(String name, double price) {}

TreeSet<Product> catalog = new TreeSet<>(
    Comparator.comparingDouble(Product::price)
              .thenComparing(Product::name)
);
catalog.add(new Product("Widget", 9.99));
catalog.add(new Product("Gadget", 24.99));
catalog.add(new Product("Donut",  9.99));

catalog.forEach(p -> System.out.println(p.name() + " $" + p.price()));
// Donut $9.99, Widget $9.99, Gadget $24.99

Pobieranie komparatora

Wywołaj comparator() na TreeMap/TreeSet, aby pobrać własny komparator. Zwraca null, jeśli używana jest kolejność naturalna.

TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
System.out.println(map.comparator() != null); // true

TreeMap<String, Integer> natural = new TreeMap<>();
System.out.println(natural.comparator());      // null

Kompozycja komparatorów

Obiekt Comparator w Javie udostępnia łańcuchowe metody fabryczne ułatwiające przejrzyste łączenie komparatorów:

Comparator<String> comp =
    Comparator.comparingInt(String::length)   // by length
              .thenComparing(Comparator.naturalOrder()); // then alphabetically

TreeSet<String> ts = new TreeSet<>(comp);
ts.addAll(List.of("go", "java", "c", "rust", "py"));
ts.forEach(s -> System.out.print(s + " "));
// c go py java rust

Kiedy NIE używać własnego komparatora

Unikaj używania w kluczach TreeMap i elementach TreeSet komparatora niezgodnego z equals — prowadzi to do mylących „brakujących” wpisów. Aby kolekcje działały poprawnie w każdym kontekście, upewnij się, że compare(a, b) == 0 zachodzi wtedy i tylko wtedy, gdy a.equals(b).

Szybkie sprawdzenie

TreeSet używa komparatora opartego wyłącznie na String::length. Co się stanie po dodaniu zarówno "cat", jak i "dog"?

Podsumowanie: niestandardowe porządkowanie

Najważniejsze informacje:

  • Przekaż Comparator do konstruktora TreeMap/TreeSet, aby zdefiniować niestandardowe porządkowanie
  • Równość zdefiniowana przez Comparator określa unikalność w kolekcjach opartych na drzewie
  • Zadbaj o zgodność komparatora z equals, aby uniknąć nieoczekiwanych rezultatów
  • Używaj Comparator.nullsFirst/nullsLast do bezpiecznego porządkowania wartości null
  • Łącz komparatory za pomocą thenComparing, aby sortować według wielu pól

Często zadawane pytania

Czy lekcja „Własny porządek w kolekcjach typu Tree” jest bezpłatna?

Tak — pełny tekst „Własny porządek w kolekcjach typu Tree” 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 „Własny porządek w kolekcjach typu Tree”?

Przekazuj Comparator do TreeMap/TreeSet, aby definiować porządek sortowania właściwy dla danej domeny. Ć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 4 z 4.

Ile czasu zajmuje lekcja „Własny porządek w kolekcjach typu Tree”?

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. TreeMap: posortowane pary klucz-wartość
  2. Podmapy i widoki zakresów
  3. TreeSet i NavigableSet
  4. Własny porządek w kolekcjach typu Tree
← Powrót do Java Academy