CodeGym /Kursy /JAVA 25 SELF /StringBuilder i StringBuffer

StringBuilder i StringBuffer

JAVA 25 SELF
Poziom 9 , Lekcja 5
Dostępny

1. Niezmienność łańcucha: przyjaciel czy wróg?

W Javie klasa Stringniezmienna (immutable). Oznacza to, że po utworzeniu łańcucha nie można go zmienić. Za każdym razem, gdy „modyfikujesz” łańcuch, na przykład dodając do niego coś przez + lub concat(), w rzeczywistości tworzony jest nowy obiekt, a stary trafia na śmietnik historii (i pamięci).

Przykład:

String s = "Hello";
s = s + " world!";
System.out.println(s); // Hello world!

Wygląda, jakby łańcuch s się zmienił, ale w rzeczywistości powstał nowy łańcuch "Hello world!", a stary "Hello" pozostaje w pamięci, dopóki nie usunie go zbieracz śmieci. Jeśli takich operacji jest dużo — na przykład w pętli — program zaczyna zwalniać i zużywać nadmiarową pamięć.

Wyobraź sobie, że budujesz wieżę z klocków i za każdym razem, gdy chcesz dodać nowy klocek, musisz przebudować całą wieżę od nowa. Mało ekonomiczne, prawda? Tak właśnie działa zwykły String w Javie przy częstych zmianach.

2. StringBuilder: szybki „budowniczy” łańcuchów

Klasa StringBuilder (pakiet java.lang, nie trzeba importować) to specjalne narzędzie do efektywnego składania i modyfikowania łańcuchów. Jest mutowalna (mutable): można dodawać, usuwać, wstawiać znaki i podciągi bez tworzenia nowego obiektu przy każdej operacji.

Analogia:
Jeśli String to betonowa płyta, to StringBuilder to klocki LEGO: dodajesz i zdejmujesz elementy do woli, nie rozbierając wszystkiego od nowa.

Jak utworzyć StringBuilder

StringBuilder sb = new StringBuilder(); // pusty
StringBuilder sb2 = new StringBuilder("Wartość początkowa");

Podstawowe metody

Metoda Opis Przykład użycia
append(...)
Dodać na koniec łańcuch, liczbę, znak itd.
sb.append("Java");
insert(index, ...)
Wstawić wartość w podanej pozycji
sb.insert(0, "Hello ");
delete(start, end)
Usunąć znaki od pozycji start (włącznie) do end (wyłącznie)
sb.delete(0, 5);
replace(start, end, str)
Zastąpić fragment łańcucha inną zawartością
sb.replace(0, 4, "Hi");
reverse()
Odwrócić łańcuch znaków
sb.reverse();
toString()
Przekształcić do zwykłego łańcucha
String s = sb.toString();
setLength(newLen)
Przyciąć lub dopełnić łańcuch do podanej długości
sb.setLength(3);

Przykład użycia

StringBuilder sb = new StringBuilder();
sb.append("Cześć, ");
sb.append("świecie!");
System.out.println(sb); // Cześć, świecie!

sb.insert(7, "Java "); // wstawimy "Java " po "Cześć, "
System.out.println(sb); // Cześć, Java świecie!

sb.replace(8, 12, "inny"); // zamienimy "Java" na "inny"
System.out.println(sb); // Cześć, inny świecie!

sb.reverse();
System.out.println(sb); // !eiceiwś ynni ,śćezC

3. StringBuffer: starszy brat z bezpieczeństwem wielowątkowym

Jaka jest różnica między StringBuilder a StringBuffer?

  • StringBuilder — szybki, ale niebezpieczny w środowisku wielowątkowym (niesynchronizowany).
  • StringBuffer — wolniejszy, ale bezpieczny dla wątków (zsynchronizowany).

Jeśli Twoja aplikacja działa w jednym wątku — używaj StringBuilder (szybszy). Jeśli wiele wątków może modyfikować ten sam łańcuch — używaj StringBuffer.

4. Kiedy używać StringBuilder zamiast String?

Scenariusze

  • Częste modyfikacje łańcucha (dodawanie, usuwanie, wstawianie) w pętli lub przy składaniu dużych tekstów.
  • Składanie łańcucha z tablicy/listy (CSV, HTML, raporty itd.).
  • Parsowanie i przetwarzanie tekstu z dużą liczbą operacji na łańcuchu.

Przykład: składanie łańcucha z tablicy

Zły sposób (przez String i +):

String[] names = {"Iwan", "Piotr", "Maria"};
String result = "";

for (int i = 0; i < names.length; i++) 
{
    result += names[i];
    if (i < names.length - 1) 
    {
        result += ", ";
    }
}
System.out.println(result);

Dobry sposób (przez StringBuilder):

String[] names = {"Iwan", "Piotr", "Maria"};
StringBuilder sb = new StringBuilder();

for (int i = 0; i < names.length; i++) 
{
    sb.append(names[i]);
    if (i < names.length - 1) 
    {
        sb.append(", ");
    }
}
System.out.println(sb.toString());

Różnica: w pierwszym przypadku na każdym kroku powstaje nowy łańcuch, w drugim — rozbudowujemy jeden obiekt StringBuilder.

5. Porównanie wydajności: String vs StringBuilder


// Przez String
long t1 = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 10000; i++)
{
    s += i + " ";
}
long t2 = System.currentTimeMillis();
System.out.println("String: " + (t2 - t1) + " ms");

// Przez StringBuilder
t1 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    sb.append(i).append(" ");
}
s = sb.toString();
t2 = System.currentTimeMillis();
System.out.println("StringBuilder: " + (t2 - t1) + " ms");

Wniosek: w większości przypadków StringBuilder działa wielokrotnie szybciej przy licznych konkatenacjach.

6. Przydatne niuanse

Czy można używać metod String?
StringBuilder ma własne metody. Aby uzyskać łańcuch — wywołaj toString().

StringBuilder sb = new StringBuilder("Hello");
String s = sb.toString(); // teraz s to zwykły łańcuch

Czy można porównywać StringBuilder przez equals?
Uwaga: sb1.equals(sb2) porównuje referencje, a nie zawartość. Porównuj tak:

if (sb1.toString().equals(sb2.toString())) 
{
    // zawartość jest taka sama
}

Czy można przekazać StringBuilder do System.out.println?
Tak. System.out.println wywoła toString() automatycznie.

Czy można odczytywać znaki po indeksie?
Tak, użyj charAt(int index), jak w zwykłym łańcuchu.

7. Typowe błędy podczas pracy ze StringBuilder i StringBuffer

Błąd nr 1: porównywanie dwóch StringBuilder przez equals lub operator ==. Te sprawdzenia porównują referencje, a nie zawartość. Użyj toString() i porównuj łańcuchy.

Błąd nr 2: zapominanie o wywołaniu toString() tam, gdzie oczekiwany jest String (zwracanie z metody, logowanie, przekazanie do API).

Błąd nr 3: używanie StringBuilder do kilku prostych konkatenacji. Zapis "Hello, " + name jest czytelny i wydajny.

Błąd nr 4: konkatenacja String w pętli przez +. To nieefektywne czasowo i pamięciowo — użyj StringBuilder.

Błąd nr 5: mylenie StringBuffer i StringBuilder bez potrzeby. Jeśli nie ma współdzielonej modyfikacji z wielu wątków — wybierz StringBuilder.

Błąd nr 6: bezrefleksyjnie przycinać zawartość StringBuilder metodą setLength(). Sprawdź poprawność nowej długości — dane po niej zostaną utracone.

1
Ankieta/quiz
Praca z napisami, poziom 9, lekcja 5
Niedostępny
Praca z napisami
Praca z napisami
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION