Sortowanie polskich napisów (Collator)
- Teksty i String
- poziom: średni
- aktualizacja: 16 sierpnia 2026
String.compareTo porównuje kody Unicode, a nie litery polskiego alfabetu — dlatego „Łódź” i „Ćmielów” lądują za „Zebrą”, choć polski alfabet umieszcza je odpowiednio między L i M oraz C i D. Użytkownik nie pomyśli wtedy „ciekawa kolejność”, tylko „ta strona jest zepsuta”. Collator zna reguły wybranego języka: kolejność a, ą, b, c, ć, potrafi ignorować wielkość liter i uznaje za równoważne różne zapisy Unicode tego samego znaku — złożone ż i z ze znakiem kropki nad literą. Dostajesz zwykły Comparator<String>, więc wepniesz go wszędzie tam, gdzie działa sort, także przy rekordach.
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
/**
* Sortowanie i porównywanie napisów według polskiego alfabetu — skryptyJAVA.pl
* Zapisz jako: PolskieSortowanie.java
*/
public final class PolskieSortowanie {
private static final Locale POLSKI = Locale.forLanguageTag("pl-PL");
/** Przykładowy obiekt sortowany najpierw po nazwisku, potem po imieniu. */
public record Osoba(String imie, String nazwisko) {}
private PolskieSortowanie() {
// Klasa zawiera wyłącznie metody statyczne.
}
/**
* Tworzy komparator zgodny z polskimi regułami. Wartość null trafia na koniec.
* SECONDARY ignoruje wielkość liter, a TERTIARY bierze ją pod uwagę.
*/
public static Comparator<String> komparator(boolean rozrozniajWielkoscLiter) {
Collator collator = Collator.getInstance(POLSKI);
collator.setStrength(rozrozniajWielkoscLiter ? Collator.TERTIARY : Collator.SECONDARY);
Comparator<String> porownanie = collator::compare;
return Comparator.nullsLast(porownanie);
}
/** Zwraca nową, posortowaną listę i nie zmienia kolekcji przekazanej przez wywołującego. */
public static List<String> sortuj(List<String> napisy) {
List<String> wynik = new ArrayList<>(napisy);
wynik.sort(komparator(false));
return wynik;
}
/**
* Porównuje napisy po polsku, ignorując wielkość liter; poprawnie obsługuje też null.
* Każde wywołanie buduje nowy Collator — w pętli sięgnij raczej po gotowy komparator.
*/
public static boolean takieSame(String pierwszy, String drugi) {
if (pierwszy == null || drugi == null) {
return pierwszy == drugi;
}
return komparator(false).compare(pierwszy, drugi) == 0;
}
/** Sortuje osoby według nazwiska i imienia, w obu polach według polskiego alfabetu. */
public static List<Osoba> sortujOsoby(List<Osoba> osoby) {
Comparator<String> teksty = komparator(false);
Comparator<Osoba> poNazwisku = Comparator.comparing(Osoba::nazwisko, teksty)
.thenComparing(Osoba::imie, teksty);
List<Osoba> wynik = new ArrayList<>(osoby);
wynik.sort(poNazwisku);
return wynik;
}
// ── PRZYKŁADY ───────────────────────────────────────────
public static void main(String[] args) {
List<String> miasta = List.of(
"Zabrze", "Łódź", "Lublin", "Śrem", "Sopot", "Ćmielów", "Cieszyn", "Żory");
List<String> unicode = new ArrayList<>(miasta);
unicode.sort(Comparator.naturalOrder());
System.out.println("Unicode: " + unicode);
System.out.println("Po polsku: " + sortuj(miasta));
System.out.println("ŁÓDŹ = łódź: " + takieSame("ŁÓDŹ", "łódź"));
System.out.println("Żaba = Z + kropka: " + takieSame("Żaba", "Z\u0307aba"));
List<Osoba> osoby = List.of(
new Osoba("Anna", "Żak"),
new Osoba("Łukasz", "Lemański"),
new Osoba("Jan", "Łuczak"),
new Osoba("Celina", "Ćwik"));
sortujOsoby(osoby).forEach(System.out::println);
}
}
Jak użyć tego skryptu
- Zapisz przykład jako
PolskieSortowanie.java— plik z klasą publiczną nosi jej nazwę — i uruchom poleceniemjava PolskieSortowanie.java. Pierwsze dwie listy pokażą różnicę między porządkiem Unicode a polskim alfabetem. - Najprościej wywołaj
PolskieSortowanie.sortuj(lista). Dostaniesz nową listę, a kolejność elementów w oryginalnej liście pozostanie bez zmian. - Do własnego sortowania pobierz
Comparator<String>przezkomparator(false)— utwórz go raz i przekazuj dalej, zamiast wołać w pętli. Przekażtrue, jeżeli wielkie i małe litery mają wpływać na kolejność. - Przy obiektach użyj komparatora jako drugiego argumentu
Comparator.comparing, a kolejne pola dołącz przezthenComparing, tak jak w metodziesortujOsoby.
Warto wiedzieć
String.compareTo,CASE_INSENSITIVE_ORDERi gołesorted()nie znają polskiego alfabetu — układają znaki według kodów Unicode. Dla identyfikatorów technicznych to w porządku, ale lista nazwisk, miast czy produktów pokazana użytkownikowi wygląda wtedy po prostu na zepsutą.- Siła
SECONDARYignoruje wielkość liter, ale zachowuje istotne różnice językowe, np. międzylił.TERTIARYdodatkowo rozróżnia wielką i małą literę.PRIMARYspłaszcza jeszcze więcej i nadaje się wyłącznie do wyszukiwania „mniej więcej”, nie do porównań. - Pułapka, o której łatwo zapomnieć: ten komparator nie jest spójny z
equalsprzy żadnej sile. Wrzuć „Kowalski”, „KOWALSKI” i „kowalski” donew TreeSet<>(komparator(false)), a zostanie jeden element — dwa przepadną bez słowa ostrzeżenia. Tak samo zachowa sięTreeMapiCollections.binarySearchz tym komparatorem. Do sprawdzania dokładnej równości używajequals;takieSameodpowiada na pytanie „czy to ten sam tekst językowo”, a nie „czy to identyczne napisy”. Collatorjest mutowalny i nie gwarantuje bezpieczeństwa wątkowego. Komparator z przykładu służy do sortowania sekwencyjnego; nie współdziel go między wątkami ani nie używaj wparallelStreamlubArrays.parallelSort— do pracy równoległej twórz osobny komparator dla każdego wątku. W obrębie jednego wątku rób odwrotnie: zbuduj komparator raz i przekazuj dalej, bo każde wywołaniekomparator(...)tworzy nowyCollator. W pętli kosztuje to około dwa razy więcej czasu niż porównania gotowym komparatorem.
Podobne skrypty
Walidacja danych: e-mail, PESEL i NIP
Zestaw gotowych metod sprawdzających adres e-mail, numer PESEL (data urodzenia, płeć i cyfra kontrolna) oraz NIP z sumą kontrolną modulo 11, z tolerancją myślników i spacji.
średni Zobacz kod → Teksty i StringPrzyjazny URL z polskich znaków
Zamienia tytuł „Żółć i Łódź!” na adres zolc-i-lodz: polskie litery rozkłada Normalizer, znaki specjalne wypadają, powtórzone separatory sklejają się w jeden, a duplikat nazwy dostaje kolejny numer.
średni Zobacz kod → Teksty i StringFormatowanie i składanie tekstu
Zestaw przykładów String.format, StringBuilder i bloków tekstu: kwoty po polsku, tabela z równymi kolumnami, skracanie zbyt długich nazw i bezpiecznie zakodowany fragment HTML.
początkujący Zobacz kod →