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, appleComparator 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 bananaSortowanie 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()); // 1TreeMap 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()); // 1Komparator 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=2Przykł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.99Pobieranie 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()); // nullKompozycja 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 rustKiedy 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
- TreeMap: posortowane pary klucz-wartość
- Podmapy i widoki zakresów
- TreeSet i NavigableSet
- Własny porządek w kolekcjach typu Tree