Java String replace - Kiedy użyć replaceAll, a kiedy StringBuilder?

Przykład użycia `java replaceAll` do zamiany "a" na "the" w tekście.

Napisano przez

Tymoteusz Sobczak

Opublikowano

4 kwi 2026

Spis treści

Temat java replace sprowadza się do kilku metod, które wyglądają podobnie, ale działają inaczej: jedne podmieniają dosłowne znaki i fragmenty, a inne uruchamiają silnik wyrażeń regularnych. W praktyce to rozróżnienie decyduje o tym, czy kod będzie krótki i czytelny, czy zacznie płatać figle przy kropkach, dolarach i spacji. Pokażę ci, kiedy użyć której metody, jak uniknąć błędów i kiedy lepiej sięgnąć po StringBuilder.

Najważniejsze różnice między metodami zamiany tekstu

  • replace(char, char) i replace(CharSequence, CharSequence) działają dosłownie, bez regexu.
  • replaceAll i replaceFirst traktują pierwszy argument jako wyrażenie regularne.
  • String jest niemutowalny, więc każda zamiana zwraca nowy obiekt.
  • Jeśli replacement ma znak $ albo \, użyj Matcher.quoteReplacement.
  • Przy wielu zmianach w jednym tekście często wygodniejszy jest StringBuilder.replace.

Najpierw wybierz między zamianą dosłowną a regexem

Ja zwykle rozdzielam ten temat na dwa światy. W pierwszym chcesz podmienić dokładnie to, co widzisz w tekście: znak, słowo, prefiks albo konkretny fragment. W drugim opisujesz wzorzec, czyli regułę typu „wszystkie spacje”, „pierwszy separator” albo „ciąg cyfr”.

Metoda Co robi Czy używa regexu Najlepsze zastosowanie Na co uważać
replace(char, char) Zamienia wszystkie wystąpienia jednego znaku Nie Proste korekty znaków, np. myślniki na podkreślenia Nie zastąpi fragmentu wieloznakowego
replace(CharSequence, CharSequence) Zamienia dosłowny fragment tekstu Nie Literalne podmiany, np. protokół, prefiks, słowo Nie zadziała jak wzorzec
replaceAll(String, String) Zamienia wszystkie dopasowania regexu Tak Normalizacja, maskowanie, zamiany oparte na regułach Łatwo pomylić zwykły tekst z regexem
replaceFirst(String, String) Zamienia pierwsze dopasowanie regexu Tak Usuwanie prefiksu, pierwszej etykiety, pierwszego separatora W replacement są znaczące $ i \
StringBuilder.replace(int, int, String) Podmienia zakres znaków w mutowalnym buforze Nie Wiele zmian w tym samym tekście, pętle, składanie wyniku Wymaga znajomości indeksów

Jeśli potrzebujesz zwykłej zamiany fragmentu, regex jest zwykle zbędny. Jeśli potrzebujesz reguły, która opisuje wiele różnych przypadków naraz, zwykły replace przestaje wystarczać. To rozróżnienie prowadzi wprost do konkretnego wyboru API, więc dalej rozbijam je na praktyczne scenariusze.

Jak działa replace bez regexu

Metody bez regexu są najbezpieczniejsze, bo robią dokładnie to, co sugeruje nazwa. replace(char, char) podmienia pojedynczy znak, a replace(CharSequence, CharSequence) działa na dosłownym ciągu znaków. Nie ma tu żadnych metaznaków, grup ani wyjątków wynikających z interpretacji wzorca.

To właśnie dlatego w prostych zadaniach wybieram je najczęściej. Jeśli chcesz zamienić myślniki na podkreślenia albo zmienić http:// na https://, nie ma sensu odpalać regexu tylko dlatego, że brzmi „bardziej profesjonalnie”.

String fileName = "raport-final-v1.txt";
String normalized = fileName.replace('-', '_');

String url = "http://jscwiczenia.pl";
String secureUrl = url.replace("http://", "https://");

Warto też pamiętać, że String w Javie jest niemutowalny. Każda z takich operacji zwraca nowy tekst, a oryginał zostaje bez zmian. Dla jednorazowej zamiany to zaleta, bo kod jest prosty i przewidywalny. Przy większej liczbie zmian ten model zaczyna jednak kosztować więcej kopii, dlatego za chwilę pokażę przykład, w którym lepiej sprawdza się StringBuilder.

Przykłady, które pokazują różnicę w kodzie

Najłatwiej zrozumieć ten temat na kilku krótkich przykładach. W praktyce nie chodzi o zapamiętanie nazw metod, tylko o to, żeby od razu widzieć, kiedy zamiana jest literalna, a kiedy opiera się na regule.

String title = "backend-dev";
String withSpaces = title.replace('-', ' ');
// "backend dev"

String path = "/api/v1/users";
String withoutLeadingSlash = path.replaceFirst("^/", "");
// "api/v1/users"

String sentence = "Ala    ma   kota";
String compact = sentence.replaceAll("\\s+", " ");
// "Ala ma kota"

Pierwszy przykład jest najprostszy: pojedynczy znak zamieniony na inny. Drugi pokazuje, że replaceFirst działa na wzorcu, więc przydaje się do wycinania pierwszego dopasowania, a nie pierwszego literalnego fragmentu. Trzeci to klasyczny case normalizacji białych znaków, gdzie regex faktycznie oszczędza kod.

Jeżeli chcesz podmienić dosłowny fragment, który sam w sobie zawiera znaki specjalne regexu, najpierw zastanów się, czy w ogóle potrzebujesz regexu. Często wystarczy zwykły replace. Gdy jednak musisz zostać przy replaceFirst lub replaceAll, wtedy wzorzec warto zacytować przez Pattern.quote(...). To samo dotyczy replacementu z dolarem lub backslashem, tylko tam używa się Matcher.quoteReplacement(...).

W skrócie: jeśli widzisz prostą, literalną podmianę, wybierasz prostą metodę. Jeśli w grę wchodzi reguła dopasowania, dopiero wtedy zaczyna się zabawa z regexem.

Kiedy replaceAll i replaceFirst naprawdę mają sens

replaceAll jest dobry wtedy, gdy chcesz zmienić wszystkie dopasowania wzorca, a nie tylko jedno konkretne wystąpienie. replaceFirst wybierasz, gdy interesuje cię wyłącznie pierwsze dopasowanie. W obu przypadkach pierwszy argument nie jest zwykłym tekstem, tylko wyrażeniem regularnym, więc musisz myśleć w kategoriach wzorców, a nie „szukaj i podmień”.

replaceAll do normalizacji i maskowania

To jest najczęstszy i najbardziej praktyczny scenariusz. W logach, formularzach i danych wejściowych często chcesz ujednolicić zapis: usunąć nadmiarowe spacje, zamaskować fragmenty danych albo przestawić kolejność segmentów tekstu.

String phone = "600-123-456";
String masked = phone.replaceAll("\\d(?=\\d{2})", "X");
// "XXX-XXX-456"

String date = "2026-06-20";
String polishDate = date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3.$2.$1");
// "20.06.2026"

Drugi przykład pokazuje, po co w ogóle istnieją grupy przechwytujące. Dzięki $1, $2 i $3 możesz przebudować wynik bez ręcznego składania stringów. To jest jeden z tych przypadków, w których regex robi realną robotę, zamiast tylko komplikować kod.

replaceFirst do wycinania tylko pierwszego dopasowania

Jeżeli chcesz usunąć prefiks, zmienić pierwszy znacznik albo zastąpić tylko pierwszy separator, replaceFirst bywa najczytelniejszy. Ja używam go wtedy, gdy dalsze wystąpienia mają już zostać nienaruszone.

String tag = "[draft] Artykuł do poprawy";
String cleaned = tag.replaceFirst("^\\[draft\\]\\s*", "");
// "Artykuł do poprawy"

W takim zadaniu nie musisz budować kilku instrukcji warunkowych. Jedna reguła regexu załatwia sprawę i od razu widać intencję kodu. Jeśli ten sam wzorzec masz zamiar wykorzystywać wielokrotnie, wtedy warto rozważyć wcześniejszą kompilację przez Pattern, żeby nie robić tego za każdym razem od nowa.

Przeczytaj również: Java Iterable - Jak działa? Różnice, przykłady i błędy

Jak nie potknąć się o znaki specjalne

Tu pojawia się najwięcej błędów. W regexie kropka oznacza „dowolny znak”, znak dolara odnosi się do grupy, a backslash ma znaczenie ucieczki. Do tego dochodzi jeszcze drugi poziom escapowania w stringach Javy. W efekcie zapis, który wygląda niewinnie, potrafi zachować się zupełnie inaczej, niż zakładasz.

import java.util.regex.Matcher;

String text = "Wartość: 49$";
String safeReplacement = Matcher.quoteReplacement("99$");
String updated = text.replaceAll("49\\$", safeReplacement);
// "Wartość: 99$"

Jeśli replacement jest generowany dynamicznie, Matcher.quoteReplacement oszczędza sporo nerwów. Używam go szczególnie wtedy, gdy tekst pochodzi z zewnętrznego źródła i nie mam pewności, czy nie zawiera znaków specjalnych. To mały detal, ale w praktyce często rozróżnia kod stabilny od kodu, który wybucha na danych z produkcji.

Ta część tematu jest ważna, bo najwięcej problemów nie wynika z samego zastąpienia tekstu, tylko z nieporozumienia między literalnym stringiem a regexem. Stąd już krok do typowych pułapek.

Najczęstsze pułapki przy zamianie tekstu

Najbardziej zdradliwe jest założenie, że wszystkie metody zamiany działają podobnie. W Java nic bardziej mylnego. Wystarczy jeden znak specjalny albo zbyt luźny wzorzec i wynik przestaje mieć cokolwiek wspólnego z tym, co chciałeś osiągnąć.

  • Kropka w regexie oznacza dowolny znak, więc replaceAll(".", "-") zamieni praktycznie cały tekst, a nie tylko kropki.
  • Replacement nie jest zwykłym stringiem w metodach regexowych, więc znak $ może odwoływać się do grupy.
  • Escapowanie działa podwójnie: raz w stringu Javy, drugi raz w regexie.
  • Zamiana działa sekwencyjnie od początku do końca, więc nakładające się fragmenty nie są traktowane jak osobny przypadek.

Dobry przykład tego ostatniego zachowania to sytuacja, w której zamieniasz aa na b w napisie aaa. Wynik będzie ba, a nie ab, bo silnik przechodzi przez tekst z lewej do prawej i nie cofa się, żeby złapać nakładające się trafienia. To drobiazg, ale potrafi całkowicie zmienić wynik przy prostych algorytmach czyszczenia danych.

Jeśli chcesz zamienić literalny znak kropki, zwykle wybierasz replace, a nie replaceAll. Jeśli chcesz użyć regexu, to lepiej od razu pisać wzorzec świadomie, zamiast liczyć, że JVM domyśli się twojej intencji. Taka dyscyplina oszczędza więcej czasu niż późniejsze debugowanie pozornie prostych tekstów.

Kiedy StringBuilder.replace daje lepszy efekt

Gdy modyfikujesz jeden tekst wielokrotnie, StringBuilder często wygrywa z łańcuchem kolejnych wywołań na String. Powód jest prosty: String jest niemutowalny, więc każda zamiana tworzy nowy obiekt. W małych przykładach to nie ma większego znaczenia, ale w pętli, przy dłuższych danych albo przy składaniu odpowiedzi HTML robi się zauważalne.

StringBuilder sb = new StringBuilder("Hello, guest! Today is draft.");
sb.replace(7, 12, "admin");
sb.replace(24, 29, "final");
// "Hello, admin! Today is final."

To narzędzie działa po indeksach, więc nie szuka tekstu za ciebie. Z tego powodu jest świetne, gdy już znasz pozycje zmian, na przykład po wcześniejszym parsowaniu stringa albo po wyliczeniu zakresów. Jeśli nie znasz indeksów i chcesz po prostu podmienić fragment, zwykły replace będzie czytelniejszy.

Warto też pamiętać, że StringBuilder jest zazwyczaj lepszym wyborem niż StringBuffer w kodzie jednowątkowym, bo nie dokłada synchronizacji, której zwykle nie potrzebujesz. To detal, ale w praktyce właśnie takie detale odróżniają wygodny kod od kodu napisanego „na wszelki wypadek”.

Co zostaje w praktyce, gdy wybierasz metodę zamiany

Jeśli miałbym sprowadzić cały temat do jednej zasady, powiedziałbym tak: najpierw sprawdź, czy potrzebujesz literalnej zamiany, a dopiero potem sięgaj po regex. W większości prostych przypadków wystarczy replace. Gdy naprawdę pracujesz na wzorcu, replaceAll i replaceFirst dają dużo większą kontrolę nad wynikiem.

  • replace(char, char) do jednego znaku.
  • replace(CharSequence, CharSequence) do dosłownego fragmentu.
  • replaceAll do wszystkich dopasowań wzorca.
  • replaceFirst do pierwszego dopasowania wzorca.
  • StringBuilder.replace do wielu zmian w jednym, mutowalnym buforze.

W kodzie produkcyjnym najbardziej cenię nie „sprytną” metodę, tylko metodę, którą da się od razu przeczytać i bez stresu utrzymać za miesiąc. Właśnie dlatego przy zamianie tekstu w Javie zwykle wygrywa prostota, a regex warto włączać tylko wtedy, gdy naprawdę rozwiązuje konkretny problem.

FAQ - Najczęstsze pytania

`replace()` działa dosłownie, zamieniając podany ciąg znaków lub znak bez interpretacji jako wyrażenia regularnego. `replaceAll()` traktuje swój pierwszy argument jako wyrażenie regularne (regex), co pozwala na bardziej złożone wzorce dopasowania.

Użyj `StringBuilder.replace()` gdy wykonujesz wiele modyfikacji tekstu. Obiekty `String` są niemutowalne, więc każda operacja `replace()` tworzy nowy obiekt. `StringBuilder` modyfikuje tekst w miejscu, co jest wydajniejsze przy wielu zmianach.

Jeśli wzorzec lub tekst zastępczy zawiera znaki specjalne regexu (np. `.`, `$`, `\`), użyj `Pattern.quote()` dla wzorca i `Matcher.quoteReplacement()` dla tekstu zastępczego. To zapewni, że znaki te będą traktowane dosłownie, a nie jako elementy regexu.

Tak, `replaceFirst()`, podobnie jak `replaceAll()`, traktuje swój pierwszy argument jako wyrażenie regularne. Zamienia jednak tylko pierwsze dopasowanie wzorca, a nie wszystkie. Jest to przydatne do usuwania prefiksów lub pierwszej instancji elementu.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

java replace java string replaceall vs replace java string replace regex java string replace przykład

Udostępnij artykuł

Tymoteusz Sobczak

Tymoteusz Sobczak

Nazywam się Tymoteusz Sobczak i mam 9-letnie doświadczenie w programowaniu webowym. Moja przygoda z tą dziedziną zaczęła się od fascynacji tworzeniem stron internetowych, co z czasem przerodziło się w pasję do dzielenia się wiedzą i pomagania innym w odkrywaniu tajników programowania. Lubię wyjaśniać złożone zagadnienia w przystępny sposób, co pozwala moim czytelnikom lepiej zrozumieć temat i rozwijać swoje umiejętności. Pisząc dla jscwiczenia.pl, koncentruję się na dostarczaniu aktualnych i rzetelnych informacji, które są zrozumiałe nawet dla osób dopiero zaczynających swoją przygodę z programowaniem. Staram się porównywać różne źródła, śledzić najnowsze trendy i organizować wiedzę w sposób, który ułatwia naukę. Moim celem jest, aby każdy mógł znaleźć tu przydatne materiały, które pomogą mu w budowaniu kariery w programowaniu webowym.

Napisz komentarz