StringBuffer vs StringBuilder

StringBuffer vs StringBuilder
0

Witam

Ostatnio podczas pisania pod Netbeans'em aplikacji która przetwarzała większe ilości tekstu, wyskoczył mi warning - że można zastąpić StringBuffer StringBuilderem.
Szybka wizyta na Google wykazała, że StringBuffer jest threadsafe, StringBuilder nie.

I tutaj pytanie - czy ktoś może wie, jak to się ma do wydajności? Skoro StringBuffer jest threadsafe to jest pewnie wolniejszy - chodzi mi tutaj przede wszystkim czy ktoś kiedykolwiek próbował łączyć większe ilości tekstu i zastąpił Buffer Builderem i zanotował, jaki przyrost wydajności to dało.

bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0

Napisałem kiedyś programik testowy do porównywania prędkości różnych sposobów sklejania Stringów. StringBuffer jest średnio 2 razy wolniejszy niż StringBuider i stosunek czasów nie zależy od ilości iteracji. Konkatenacja Stringów (operator +) jest dramatycznie wolniejsza (tysiące razy).


To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
Antoniossss
  • Rejestracja:prawie 16 lat
  • Ostatnio:ponad 10 lat
0

Generalnie według dokumentacji, jedno i drugie to "to samo" w przenośni, jednak różni się synchronizacją. Dlatego też, jeżeli nie bawisz się w wielowątkowe sklejanie stringów, zawsze używaj string buildera. Na potwierdzenie moich słów cytacik

As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder.The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.

i znowu potwierdzenie tego co napisałem - różnica polega tylko na synchronizacji - co jest czasochłonne. Pozdro!
@bogdans
A patrzyłeś jak to się ma do zjadania pamięci, alokacji char[] i wycieków? Aż mnie to zaciekawiło :)


www.toptraker.pl
TopTraker! - I wiesz co jest grane!
Mój własny projekt w Javie - najnowsza wersja już jest!
bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0

Żadnego profilera nie używałem, windowsowy TaskManager nie wykazywał żadnego zauważalnego zwiększenia zużycia pamięci, np. podczas wykonywania takiej pętli

Kopiuj
String zwrot="";
for (int i=1;i<=1000000;i++)
{
     zwrot+="A";
}

To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
OA
Prawdopodobnie kompilator niejawnie użył tutaj klasy StringBuilder.
bogdans
Na pewno nie, w tym samym programie pętla StringBuilder zwrot=new StringBuilder(""); for (int i=1;i<=1000000;i++) { zwrot.append("A"); }wykonywała się 0,03 s, a podana w poście 7400 sekund
Antoniossss
@Oak z tego co się orientuje, kompilator niejawnie używa StringBuildera w "inline"'ach tzn zwykłych, pojedynczych(lub wielokrotnych) konkatenacjach jednak nie w pętli.
OA
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 10 lat
  • Postów:95
2

Łatwy sposób sprawdzenia:

Kopiuj
class StringTest {
	public String toString() {
		String zwrot = "";
		for (int i = 1; i <= 1000000; i++)
			zwrot += "A";
		return zwrot;
	}
}

Kompilujemy, a następnie wydajemy polecenie:
javap -c StringTest
Zobaczymy coś takiego:

Kopiuj
class StringTest {
  StringTest();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public java.lang.String toString();
    Code:
       0: ldc           #2                  // String 
       2: astore_1      
       3: iconst_1      
       4: istore_2      
       5: iload_2       
       6: ldc           #3                  // int 1000000
       8: if_icmpgt     37
      11: new           #4                  // class java/lang/StringBuilder
      14: dup           
      15: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      18: aload_1       
      19: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: ldc           #7                  // String A
      24: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      30: astore_1      
      31: iinc          2, 1
      34: goto          5
      37: aload_1       
      38: areturn       
}

Widać, że kompilator użył klasy StringBuilder. Ale nie jest na tyle inteligentny, aby wyciągnąć tworzenie obiektu tej klasy przed pętlę.

edytowany 2x, ostatnio: Oak
Antoniossss
aha ok, no to nowe info do bazy danych :)
bogdans
A o czym ten listing świadczy? Kod wykonuje się piekielnie długo. Po zmianie w kodzie String na StringBuilder oraz '+' na append() przyspiesza tysiące razy. Zapewne kompilator zamienia zwrot+"A"; na: sb=new StringBuilder(zwrot); sb.append("A"); zwrot=sb.toString(); Wątpliwa optymalizacja.
OA
Napisałem przecież "Ale nie jest na tyle inteligentny, aby wyciągnąć tworzenie obiektu tej klasy przed pętlę". Sam chciałem się dowiedzieć, co dokładnie się dzieje, więc wkleiłem zdekompilowany kod bajtowy jaki tworzy kompilator.
iooi
Bo przecie operator + na Stringu to tylko cukier składniowy zamieniany na to, co widać na listingu.
bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0

Będę precyzyjny, u mnie było tak:

Kopiuj
...
int iteracje = Integer.parseInt(args[0])
...
powiel1("A",iteracje);
...
powiel3("A",iteracje);
...
private static String powiel1(String s,int ile)
{
    String zwrot="";
    for (int i=1;i<=ile;i++)
    {
       zwrot+=s;
    }
    return zwrot;
}
private static String powiel3(String s,int ile)
{
    StringBuilder zwrot=new StringBuilder("");
    for (int i=1;i<=ile;i++)
    {
       zwrot.append(s);
    }
    return zwrot.toString();
}

i program był wywołany z parametrem 1000 000.


To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
edytowany 1x, ostatnio: bogdans
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:25 minut
0

Przede wszystkim referencyjny JVM od Javy 6 ma wbudowany Lock Elision: http://www.ibm.com/developerworks/java/library/j-jtp10185/index.html i może sam omijać synchronizację w pewnych przypadkach. Aby mieć pewność co do wpływu synchronizacji na szybkość pasowałoby porównać StringBuildera i StringBuffera na jakimś JVMie, który nie wspiera Lock Elision. Może Dalvik nie ma?


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.