2013-02-18 11 views
40

W this answer, mówi (sugeruje), że konkatenacja ciągów jest zoptymalizowana do operacji StringBuilder tak czy inaczej, więc kiedy piszę mój kod, czy istnieje jakiś powód, aby napisać kod StringBuilder w źródle? Zauważ, że mój przypadek użycia różni się od pytania OP, ponieważ łączę/załączam setki tysięcy linii.Java: String concat vs StringBuilder - zoptymalizowany, więc co powinienem zrobić?

Aby być bardziej zrozumiałym: Jestem dobrze poinformowany o różnicach każdego z nich, po prostu nie wiem, czy warto pisać kod StringBuilder, ponieważ jest on mniej czytelny i kiedy jego rzekomo wolniejszy kuzyn, klasa String, jest konwertowane automagicznie w procesie kompilacji.

+1

Oto dobra analiza: http://www.javacodegeeks.com/2013/03/java-stringbuilder-myth-debunked.html – marcolopes

Odpowiedz

105

myślę wykorzystanie StringBuilder vs + naprawdę zależy od kontekstu używasz go.

Ogólnie użyciu JDK 1.6 i powyżej kompilator automatycznie dołączyć ciągi razem używając StringBuilder.

String one = "abc"; 
String two = "xyz"; 
String three = one + two; 

To będzie skompilować String three jak:

String three = new StringBuilder().append(one).append(two).toString(); 

Jest to bardzo pomocne i oszczędza nam czas pracy. Jednak ten proces nie zawsze jest optymalny. Weźmy na przykład:

String out = ""; 
for(int i = 0; i < 10000 ; i++) { 
    out = out + i; 
} 
return out; 

Jeśli skompilować do kodu bajtowego, a następnie dekompilacji kodu bajtowego generowane otrzymujemy coś takiego:

String out = ""; 
for(int i = 0; i < 10000; i++) { 
    out = new StringBuilder().append(out).append(i).toString(); 
} 
return out; 

Kompilator został zoptymalizowany wewnętrznej pętli, ale z pewnością nie popełnił najlepszych możliwych optymalizacje .Aby poprawić nasz kod możemy użyć:

StringBuilder out = new StringBuilder(); 
for(int i = 0 ; i < 10000; i++) { 
    out.append(i); 
} 
return out.toString(); 

Teraz jest to bardziej optymalne niż kompilator wygenerowany kod, więc nie jest to z pewnością trzeba pisać kod za pomocą StringBuilder/StringBuffer zajęcia w przypadkach, gdy wymagana jest wydajny kod. Obecne kompilatory nie radzą sobie z łączeniem łańcuchów w pętlach, jednak może się to zmienić w przyszłości.

Należy uważnie sprawdzić, gdzie trzeba ręcznie zastosować StringBuilder i spróbować go użyć, jeśli nie zmniejszy to czytelności kodu.

Uwaga: Skompilowałem kod przy użyciu JDK 1.6 i dekompilowałem kod przy pomocy programu javap, który wypisuje kod bajtowy. Jest dość łatwy do zinterpretowania i często jest użytecznym odniesieniem do próby optymalizacji kodu. Kompilator zmienia kod za kulisami, więc zawsze jest ciekawie zobaczyć, co robi!

+3

Czy Twój kod dekompilowany (w drugiej pętli for) jest wynikiem rzeczywistej dekompilacji, czy to jest twoje przypuszczenie? Jeśli debugujesz pętlę for/while ze sprzężeniem ciągów "+", zauważysz, że obiekt StringBuilder nie jest odtwarzany w każdej pętli, ale w rzeczywistości ten sam jest ponownie wykorzystywany. W przeciwnym razie, jeśli zastępujesz cięższy obiekt (np. StringBuilder for String), jaką optymalizację otrzymujesz naprawdę? –

+2

Chciałbym również znać odpowiedź na pytanie @SpencerKormos. Czy kompilator faktycznie tworzy nowy 'StringBuilder' dla każdej iteracji pętli? Dlaczego miałoby to być optymalne? – ryvantage

+0

Odtworzenie odpowiedzi i odnośnik w pytaniu OP, punkt, który robił Tom, polegał na tym, że jeśli operacja concat odbywa się w pętli, to nie ma optymalizacji przez kompilator na ogólnym bloku kodu (który zawierałby dla pętla). W takim przypadku możesz zadeklarować swój StringBuilder * przed * pętlą for i użyć append() w pętli, dzięki czemu operacja konkatycji będzie najbardziej wydajna (tj. Ostatni blok kodu od Toma). Mam nadzieję, że ma to sens, i dzięki za przywrócenie mnie do tego! –

0

Mogłem źle zrozumieć twoje pytanie, ale StringBuilder działa szybciej podczas dołączania ciągów znaków. Tak, tak, jeśli dodajesz "setki tysięcy linii", zdecydowanie powinieneś użyć StringBuilder (lub StringBuffer, jeśli używasz aplikacji wielowątkowej).

(Dokładniejsza odpowiedź w komentarzach)

+1

Najwyraźniej nowoczesne/dojrzałe kompilatory Java są wystarczająco inteligentne, aby konwertować operacje konkatenacji na operacje dołączania StringBuilder, więc jaki jest sens używania StringBuilder w kodzie, gdy jest on mniej czytelny, a gdy konkrety String są w ogóle konwertowane na operacje StringBuilder? – Soyuz

+2

Och, teraz widzę twój punkt, kolego. Tak, masz rację: kompilator rozpozna szansę na zoptymalizowanie twojego kodu i zastąpi zwykły String.concat przez StringBuilder przez większość czasu - ale nie zawsze. Nie można polegać na kompilatorze. Jeśli kod jest złożony, kompilator może przegapić możliwość optymalizacji. Gdybym był tobą, wybrałbym uczynienie mojego kodu nieco mniej czytelnym ze względu na zapewnienie optymalizacji. – cldjr

2

To zależy od przypadku, ale StringBuilder jest uważana za nieco szybciej. Jeśli robisz konkatenację wewnątrz pętli, sugerowałbym użycie StringBuilder.

W każdym razie radziłbym profilować i porównywać kod (jeśli robisz tak masywny dodatek).

Bądź jednak ostrożny: instancje StringBuilder są zmienne i nie mogą być współużytkowane między wątkami (chyba, że ​​naprawdę wiesz, co robisz.) W przeciwieństwie do String, które są niezmienne.

+0

Przeredaguj pytanie. Jestem świadomy tego, co powiedziałeś, mam na myśli, co właściwie powinienem napisać? – Soyuz

+0

Naprawdę powinien istnieć jakiś dowód lub źródło kopii zapasowej "StringBuilder jest uważany za nieco szybszy". Zakładam, że dzieje się tak z powodu braku synchronizacji, ale mimo wszystko - źródła byłyby dobre. –

3

Kluczową frazą w pytaniu jest "rzekomo wolniej". Musisz określić, czy to rzeczywiście wąskie gardło, a następnie zobaczyć, które jest szybsze.

Jeśli masz zamiar napisać ten kod, ale jeszcze go nie napisałeś, napisz to, co dla ciebie bardziej przejrzyste, a następnie, jeśli trzeba, sprawdź, czy jest to wąskie gardło.

Podczas gdy sensowne jest użycie kodu, który uważasz za bardziej podobny do tego, by był szybszy, jeśli oba są jednakowo czytelne, faktyczne znalezienie czasu, który jest szybszy, gdy nie potrzebujesz, jest stratą czasu. Czytelność powyżej wydajności aż do wydajności jest nie do przyjęcia.

+0

Właściwie po (just) przepisanie kodu w obie strony, jest niewielka różnica w czytelności, ponieważ jest to po prostu zmiana deklaracji klasy i włączenie concat() do append(). Sądzę, że będę trzymać się StringBuilder, aby zapewnić wtedy wydajność. – Soyuz

Powiązane problemy