Dlaczego StringBuilder, kiedy istnieje String?

82

Dopiero co spotkałem StringBuildersię po raz pierwszy i byłem zaskoczony, ponieważ Java ma już bardzo potężną Stringklasę, która umożliwia dołączanie.

Dlaczego druga Stringklasa?

Gdzie mogę dowiedzieć się więcej StringBuilder?

an00b
źródło
Chciałem tylko zauważyć, że kiedyś pojawiło się to jako pytanie do wywiadu. Zapytali, co chciałbym zapełnić dużym
Stringiem

Odpowiedzi:

171

Stringnie pozwala na dołączanie. Każda metoda wywoływana na a Stringtworzy nowy obiekt i zwraca go. Dzieje się tak, ponieważ Stringjest niezmienna - nie może zmienić swojego stanu wewnętrznego.

Z drugiej strony StringBuilderjest zmienny. Kiedy wywołujesz append(..), zmienia wewnętrzną tablicę znaków, zamiast tworzyć nowy obiekt ciągu.

Dlatego bardziej efektywne jest posiadanie:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500; i ++) {
    sb.append(i);
}

zamiast str += i, co spowodowałoby utworzenie 500 nowych obiektów łańcuchowych.

Zauważ, że w przykładzie używam pętli. Jak zauważa helios w komentarzach, kompilator automatycznie tłumaczy wyrażenia takie String d = a + b + cjak np

String d = new StringBuilder(a).append(b).append(c).toString();

Zauważ również, że istnieje StringBufferdodatek do StringBuilder. Różnica polega na tym, że ta pierwsza ma zsynchronizowane metody. Jeśli używasz go jako zmiennej lokalnej, użyj StringBuilder. Jeśli zdarzy się, że jest możliwe, aby uzyskać do niego dostęp przez wiele wątków, użyj StringBuffer(to rzadsze)

Bozho
źródło
25
+1. Możesz dodać: „stąd StrungBuilder szuka wydajności” i „kompilatory Java zastępują wyrażenia, takie jak A + B + C, nowym StringBuilder (A) .append (B) .append (C) .toString (), aby uniknąć wydajności tworzenia obiektów kary ":)
helios
i wszystkim bardzo dziękuję. Wszyscy zasługują na +1 (które zostaną dostarczone niezwłocznie :)
an00b
super jak przywołanie „niezmiennych” obiektów.
Gary Tsui
Świetne odpowiedzi. Tęsknię jednak za powodem, dla którego nie możemy po prostu pozwolić kompilatorowi dowiedzieć się, kiedy przez większość czasu używać Stringbuildera , w tym pętli in for, aby nie trzeba było o tym myśleć jako o programistach. :)
worldsayshi
Dobra odpowiedź . Chciałbym dodać te wiersze: Ciąg jest niezmienny, ponieważ tablica (tj. Char [] wartość), która przechowuje ciąg jest zadeklarowana jako ostateczna, ale w przypadku StringBuilder tablica (tj. Wartość char []), która przechowuje ciąg nie jest ostateczna. Możesz dokonać zmian w tablicy, która przechowuje łańcuch w przypadku Stringbuildera
Deen John
61

Oto konkretny przykład, dlaczego -

int total = 50000;
String s = ""; 
for (int i = 0; i < total; i++) { s += String.valueOf(i); } 
// 4828ms

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < total; i++) { sb.append(String.valueOf(i)); } 
// 4ms

Jak widać różnica w wydajności jest znacząca.

Amir Raminfar
źródło
Ps. Uruchomiłem to na moim Macbook Pro Dual core.
Amir Raminfar
25
To wyjaśnia, dlaczego StringBuilder, gdy istnieje String? To nie wyjaśnia, dlaczego StringBuilder jest tak szybki. ale to nie jest kwestia. Więc to jest prawidłowa odpowiedź.
Kerem Baydoğan
2
@krmby - Zgoda. Aby odpowiedzieć, dlaczego tak naprawdę jest przeznaczone na inne pytanie.
Amir Raminfar
12
Myślę, że aby porównanie było uczciwe, s = sb.ToString();na koniec należy uwzględnić czas na wykonanie a, więc przynajmniej zrobiłeś to samo w obu przykładach (wynik to a string).
Scott Whitlock
19

Klasa String jest niezmienna, podczas gdy StringBuilder jest zmienna.

String s = "Hello";
s = s + "World";

Powyższy kod utworzy dwa obiekty, ponieważ ciąg znaków jest niezmienny

StringBuilder sb = new StringBuilder("Hello");
sb.append("World");

Powyższy kod utworzy tylko jeden obiekt, ponieważ StringBuilder nie jest niezmienny.

Lekcja: Ilekroć istnieje potrzeba wielokrotnego manipulowania / aktualizowania / dołączania String, przejdź do StringBuilder jako wydajnego w porównaniu do String.

unk1102
źródło
8

StringBuilder służy do budowania ciągów. A konkretnie budowanie ich w bardzo wydajny sposób. Klasa String jest dobra do wielu rzeczy, ale w rzeczywistości ma naprawdę straszną wydajność podczas składania nowej struny z mniejszych części struny, ponieważ każda nowa struna jest całkowicie nową, ponownie przydzieloną struną. (Jest to niezmienne ) StringBuilder utrzymuje taką samą sekwencję w miejscu i modyfikuje go ( modyfikowalnych ).

Rex M
źródło
5

Klasa StringBuilder jest zmienna iw przeciwieństwie do String, umożliwia modyfikowanie zawartości ciągu bez konieczności tworzenia większej liczby obiektów String, co może zwiększyć wydajność, gdy mocno modyfikujesz ciąg. Istnieje również odpowiednik dla StringBuilder o nazwie StringBuffer, który jest również zsynchronizowany, więc jest idealny dla środowisk wielowątkowych.

Największym problemem związanym z String jest to, że każda operacja, którą na nim wykonasz, zawsze zwróci nowy obiekt, na przykład:

String s1 = "something";
String s2 = "else";
String s3 = s1 + s2; // this is creating a new object.
CarlosZ
źródło
4

StringBuilder jest dobry, gdy masz do czynienia z większymi ciągami. Pomaga poprawić wydajność.

Oto artykuł , który okazał się pomocny.

Szybkie wyszukiwanie w Google mogłoby Ci pomóc. Teraz zatrudniłeś 7 różnych osób, aby wyszukały za Ciebie w Google. :)

Vanchinathan Chandrasekaran
źródło
Czy nie wszyscy tutaj wykonujemy nieodpłatną pracę?
CA Arefe,
4

Mówiąc dokładniej, dodanie wszystkich ciągów StringBuilder to O (N), a dodanie String to O (N ^ 2). Sprawdzanie kodu źródłowego odbywa się wewnętrznie poprzez zachowanie zmiennej tablicy znaków. StringBuilder wykorzystuje technikę duplikacji długości tablicy, aby osiągnąć amortyzowaną wydajność O (N ^ 2), kosztem potencjalnego podwojenia wymaganej pamięci. Możesz wywołać trimToSize na końcu, aby rozwiązać ten problem, ale zwykle obiekty StringBuilder są używane tylko tymczasowo. Możesz dodatkowo poprawić wydajność, zapewniając dobre początkowe przypuszczenie ostatecznego rozmiaru ciągu.

mahoń
źródło
3

Wydajność.

Za każdym razem, gdy łączysz ciągi, zostanie utworzony nowy ciąg. Na przykład:

String out = "a" + "b" + "c";

Tworzy to nowy, tymczasowy ciąg, kopiuje do niego „a” i „b”, dając w wyniku „ab”. Następnie tworzy kolejny nowy, tymczasowy ciąg, kopiuje do niego „ab” i „c”, dając w wyniku „abc”. Wynik ten jest następnie przypisywany do out.

Rezultatem jest algorytm Schlemiela the Paintera o złożoności czasowej O (n²) (kwadratowej).

StringBuilderz drugiej strony umożliwia dołączanie ciągów w miejscu, zmieniając rozmiar ciągu wyjściowego w razie potrzeby.

Tomasz
źródło
Wiele implementacji JVM skompiluje Twój przykład do StringBuilder, a następnie przekonwertuje wynik końcowy na String. W takim przypadku nie zostanie złożony przez powtarzające się alokacje String.
scottb
1

Java ma String, StringBuffer i StringBuilder:

  • Ciąg: jest niezmienny

  • StringBuffer: jego Mutable i ThreadSafe

  • StringBuilder: jego Mutable, ale nie ThreadSafe, wprowadzone w Javie 1.5

Ciąg np:

public class T1 {

    public static void main(String[] args){

        String s = "Hello";

        for (int i=0;i<10;i++) {

            s = s+"a";
            System.out.println(s);
        }
    }
}

}

wyjście: zostanie utworzonych 10 różnych ciągów zamiast tylko 1 ciągu.

Helloa
Helloaa
Helloaaa
Helloaaaa
Helloaaaaa
Helloaaaaaa
Helloaaaaaaa
Helloaaaaaaaa 
Helloaaaaaaaaa 
Helloaaaaaaaaaa

StringBuilder np: Zostanie utworzony tylko 1 obiekt StringBuilder.

public class T1 {

    public static void main(String[] args){

        StringBuilder s = new StringBuilder("Hello");

        for (int i=0;i<10;i++) {    
            s.append("a");
            System.out.println(s);
        }
    }
}
Kumar Vivek Mitra
źródło