wann sollte man denn StringBuilder verwenden? Bzw. ab wievielen String operationen? Lässt sich das irgendwie festmachen? z.B. habe ich das hier String test = bla1 + bla2 + bla3 +bla4 + bla5 + bla6 + bla7 + bla8
wäre das besser wenn ich die ganzen blas in einer for-schleife einem Stringbuilder anhänge?
Hm... @Leroy42 der Unterschied zwischen einer String-Addition und einem StringBuilder kann schon deutlicher sein, als man das im ersten Moment vermuten würde...
@KLeines: Ob sich das "lohnt" oder es notwendig ist, kann man nicht pauschal sagen. Wenn du so einen String einmal in der GUI anzeigst, oder auf die Festplatte schreibst: Vergiß es, da kommt's auf 10 Millisekunden nicht an. Wenn es aber WIRKLICH ..."1 Milliarde mal" (oder auch nur z.B. 1000 Mal pro Sekunde) gemacht werden soll, könnte sich ein StringBuilder lohnen....
Hm... @Leroy42 der Unterschied zwischen einer String-Addition und einem StringBuilder kann schon deutlicher sein, als man das im ersten Moment vermuten würde...
Also ich habe jetzt keine genauen Benchmarks, aber der Einsatz des StrinBuilders rentiert sich schon wesentlich schneller , sowohl was die Verarbeitungsgeschwindigkeit als auch den Speicherverbrauch angeht:
Zeichenketten, die in der virtuellen Maschine in String-Objekten gespeichert sind, haben die Eigenschaft, dass ihr Inhalt nicht mehr verändert werden kann. Anders verhalten sich die Exemplare der Klasse StringBuffer und StringBuilder, an denen sich Veränderungen vornehmen lassen. Die Veränderungen betreffen anschließend das StringBuffer/StringBuilder-Objekt selbst, und es wird kein neu erzeugtes Objekt als Ergebnis geliefert, wie zum Beispiel beim Plus-Operator und der concat()-Methode bei herkömmlichen String-Objekten. Sonst sind sich aber die Implementierung von String-Objekten und StringBuffer/StringBuffer-Objekten ähnlich.
Ich hatte da letztes Jahr einmal ein Aha-Erlebnis beim Durchwursten einer größeren Datenmenge (ein paar Millionen Datensätze). Ich habe die Ergebnisse jeweils in einzelnen Zeilen an einen String angehängt. Als bei einfachem Anhängen an einen String das Programm auch nach einer Minute noch nicht fertig war habe ich stattdessen noch einmal einen StringBuilder verwendet und das Programm war innerhalb weniger Sekunden fertig.
StringBuilder ist nicht langsamer als string concatenation mittels des +-operators. es spricht nichts dagegen, immer StringBuilder den vorzug zu geben.
allerdings spielt StringBuilder seinen vorteil erst dann wirklich aus, wenn fortwährend an einen string teile angefügt werden.
die beiden schleifen:
Code:
for( int i = 0; i < 1000; ++i)
{
// stuff
String s = "a" + "b";
}
for( int i = 0; i < 1000; ++i)
{
// stuff
String s = new StringBuilder("a").append("b").toString();
}
werden sich in ihrer performance kaum unterscheiden.
... nur dass beim Konkatenieren in diesem Fall 1000 String-Objekte entstehen, die dann erst wieder entsorgt werden müssen. Das ist im konkreten Beispiel zwar auch mit dem StringBuilder der Fall, aber man könnte es eben auch so machen, dass nur ein Objekt entsteht.
In welcher Situation ist die +-Konkatenation denn schneller als ein StringBuilder? Ich dachte, der Compiler würde die +-Konkatenation ohnehin auf einen StringBuilder abbilden.
ime kann er in Schleifen keinen String zu einem StringBuilder machen, ansonsten schon.
Dieses Thema hatten wir schon mal
Hab damals einen kleinen Microbenchmark geschrieben der zufällig meine These stützt *g*
Hier der Microbenchmark:
Code:
package foo;
public class PerfTest {
public static void main(String[] args) throws Exception {
String str = "";
int durchgänge= 100000;
long startTime= System.nanoTime();
long timeElapsed;
System.out.println( "For-Schleife mit String: " );
for(int i = 0; i < durchgänge; i++)
str += "x";
timeElapsed= System.nanoTime() - startTime;
System.out.println( timeElapsed );
str = "";
System.out.println( "For-Schleife mit StringBuilder: " );
startTime= System.nanoTime();
StringBuilder strB = new StringBuilder();
for(int i = 0; i < durchgänge; i++)
strB = strB.append("x");
timeElapsed= System.nanoTime() - startTime;
System.out.println( timeElapsed );
str= "";
System.out.println( "For-Schleife mit Funktionsaufruf und String: " );
startTime= System.nanoTime();
for(int i = 0; i < durchgänge; i++)
str= concatWithStrings( str, "x" );
timeElapsed= System.nanoTime() - startTime;
System.out.println( timeElapsed );
startTime= System.nanoTime();
System.out.println( "For-Schleife mit Funtkionsaufruf und StringBuilder: " );
for(int i = 0; i < durchgänge; i++)
str = concatWithStringBuilder( str, "x" );
timeElapsed= System.nanoTime() - startTime;
System.out.println( timeElapsed );
}
final static String concatWithStrings( final String str1, final String str2 ) {
return str1 + str2;
}
final static String concatWithStringBuilder( final String str1, final String str2 ) {
StringBuilder result= new StringBuilder();
result.append( str1 ).append( str2 );
return result.toString();
}
}
Starte ich das und will das die Client VM benutzt wird, läuft es so:
java -client foo.PerfTest
For-Schleife mit String:
20744478697
For-Schleife mit StringBuilder:
3734762
For-Schleife mit Funktionsaufruf und String:
20736109407
For-Schleife mit Funtkionsaufruf und StringBuilder:
300872102320
Im Gegensatz dazu dasselbe nochmal mi der Server VM:
java -server foo.PerfTest
For-Schleife mit String:
17666163926
For-Schleife mit StringBuilder:
7359525
For-Schleife mit Funktionsaufruf und String:
17743117701
For-Schleife mit Funtkionsaufruf und StringBuilder:
100023550340
Tja, plötzlich sind Strings viel schneller, zumindest wenn die Verkettung in der Methode passiert und dabei ein neuer StringBuilder und String (result.toString) erzeugt werden muss.
Ich finde, man sollte immer so schreiben, das man es einfacher lesen kann.
Falls mein Programm dann zu langsam läuft, brauch ich einen Profiler um herauszufinden, was genau zu langsam ist, man erinnere sich an die 80-20 Regel (oder besser 95-5 Regel), die meisten Ressourcen verbraucht nur ein Bruchteil eines Programmes.
Es wird also in beiden Fällen der gleiche Code erzeugt; die Verwendung des StringBuilders bringt zwar keinen Vorteil, aber definitiv auch keinen Nachteil.
Einen Vorteil bringt die explizite Verwendung eines StringBuilders immer dann, wenn man verhindern kann, dass während des Hinzufügens der interner Buffer überläuft und daher neu allokiert und umkopiert werden muss. In diesem Beispiel kann man das erreichen, wenn man den StringBuilder gleich groß genug allokiert:
Code:
public String t3( String s1, String s2) {
return new StringBuilder( s1.length() + s2.length()).append( s1).append( s2).toString();
}
Ob sich der Aufwanbd für die zusätzlichen length()-Aufrufe lohnt, hängt davon ab, wie groß die Strings sind: sind beide zusammen nicht länger als die Default-Läne des StringBuilders, dann lohnt es sich auf keinen Fall.
Ganz einfach ist das also nicht zu entscheiden; da die Lesbarkeit des Codes durch Verwendung des StringBuilders ja nicht besser wird, sollte man m.E. nicht generell überall die +-Konkatenation durch StringBuilder ersetzen. Wo aber von Anfang an klar ist, dass sich hier ein Bottleneck ergeben wird (also z.B. in häufig durchlaufenen Schleifen), da sollte man das wohl tun.
du solltest fair spielen, z.B.
str= "";
vor 'For-Schleife mit Funtkionsaufruf und StringBuilder'
aber auch dann ist der StringBuilder natürlich langsamer, wenn du ihn mit Größe 16 initialisiert,
besonders realistisch ist das allerdings nicht,
schreibe
StringBuilder result = new StringBuilder(str1.length()+str2.length());
und schon ist der StringBuilder sogar wieder etwas schneller
Es wird also in beiden Fällen der gleiche Code erzeugt; die Verwendung des StringBuilders bringt zwar keinen Vorteil, aber definitiv auch keinen Nachteil.
wenn ich einen 100.000 zeichen langen string habe und ich in einer schleife 1000 mal ein zeichen anhänge, dann hab ich tausend 100.000 zeichen lange strings im speicher.
Ich bin der Meinung, dass das String zusammenhängen kein primäres performance problem ist, sonder ein speicher problem. Ein performance Probelm wirds erst wenn der GC los startet.
btw: ich benutz immer StringBuffer, gibts da nennenswerte unterschiede?
Ich bin der Meinung, dass das String zusammenhängen kein primäres performance problem ist, sonder ein speicher problem. Ein performance Probelm wirds erst wenn der GC los startet.
Ich bin der Meinung, dass das String zusammenhängen kein primäres performance problem ist, sonder ein speicher problem. Ein performance Probelm wirds erst wenn der GC los startet.
Würde ich so nicht sagen; auch das Allokieren des Speichers für die neu angelegten String-Objekte kostet bereits Zeit, nicht erst das Wegräumen durch den GC.
Wandelt Java ab 1.6 die strings additionen jetzt nicht in einen Stringbuilder um in einer for-schleife?
wäre dieser code also für die Katz? und könnte mit strings bla+bla genauso schnell sein?
Code:
public StringBuilder generateSpace(int number)
{
StringBuilder space = new StringBuilder();
for (int i = 0 ; i < number; i++)
{
space.append(' ');
}
return space;
}
deshalb nennt man das beispiel. es ging mir darum zu zeigen, dass bei erzeugung und kurzfristiger verwerfung lokaler objekte die verwendung von + oder buffer keine rolle spielt.
So Freunde um das mal auch für Schleifen zu klären, habe ich folgenden Code geschrieben:
Code:
public static void main(String[] args) {
String s = "";
StringBuilder sb = new StringBuilder();
for (int i = 10000; i > 0; i--) {
s += "a";
}
for (int i = 10000; i > 0; i--) {
sb.append("a");
}
}
Compiliert und wieder De-compiliert - und voilá:
Code:
public static void main(String args[]) {
String s = "";
StringBuilder sb = new StringBuilder();
for (int i = 10000; i > 0; i--)
s = (new StringBuilder(String.valueOf(s))).append("a").toString();
for (int i = 10000; i > 0; i--)
sb.append("a");
}
Auch s += s2 in Schleifen wird in einen StringBuilder gekapselt.
Jetzt das Aber:
Es werden dazu aber auch i neue StringBuilder Objekte angelegt, dann wird sich jedes mal die Stringrepräsentation von String s geholt, dann kommt erst ein append und zum Schluss noch einmal alles in einen String konvertiert - wie gesagt das alles i-mal.
Variante gleich mit StringBuilder:
Enthält lediglich i appends.
Dies dürfte die Laufzeitunterschiede beider Varianten erklären.
[edit]
Das führt nun eigentlich zu folgendem Fazit:
Wird ein String nicht als Konstante verwendet, nimmt man StringBuilder.
1. Nebenregel
Findet eine Konkatierung von Strings ausserhalb von Schleifen statt, so verwendet man der Lesbarkeit halber + anstatt append, da intern sowieso auf StringBuilder und append "optimiert" wird. (siehe mein Beispiel mit MouseWheelListener)
[/edit]
> Auch s += s2 in Schleifen wird in einen StringBuilder gekapselt.
was soll das denn, wieso nicht?
denkst du Java schaut erst nach, ob es sich in einer Schleife befindet,
bevor es jedes s + s optimiert?
nein, das geht automatisch selbstverständlich auch in einer Schleife,
dieses ' i neue StringBuilder Objekte angelegt' war noch die Frage, und das ist nunmal nicht ganz so leicht zu optimieren
danke dir für die Tests, also verwende ich in Zukunft aus speed/ressourcengründen nur noch Stringbuilder in der for-schleife egal wieviel Datensätze, um mir die SB Klasse anzugewöhnen
Hatte da vor einem Monat erst was,
habe ne Datei Zeilenweise eingelesen while(... != EOF) { s += ... }
bei dateien >20kb wurde das nach minuten nicht fertig(100kb file hat ca 30min gedauert, wobei mit dem string ja dann auch noch was gemacht wurde)
habe mich dann auch hilfesuchend an andere gewendet und den tipp mit StringBuilder bekommen,
siehe da , plötzlich lief alles in nichtmal ner Sekunde =)
@Wolfgang:
was jeder Compiler/ Interpreter draus macht kann man nicht unbedingt vorhersagen,
aber ein Beispiel:
Code:
public class Test
{
public static void main(String[] args)
throws Exception
{
StringBuilder builder = new StringBuilder(1000);
String a = "Wird hier zunächst ein ";
String b = "1234567890123";
for (int i = 0; i < 8; i++)
{
long time = System.currentTimeMillis();
for (int j = 0; j < 2000000; j++)
{
builder.append(a + b);
builder.setLength(0);
}
System.out.println(b.length() + ", Time: " + (System.currentTimeMillis() - time));
b += "x";
}
}
}
Ausgabe:
13, Time: 906
14, Time: 875
15, Time: 906
16, Time: 953
17, Time: 1375
18, Time: 1313
19, Time: 1359
20, Time: 1422
das Programm wird langsamer, wenn der zweite String b > 16 Zeichen lang ist,
und zwar weil die Strings a und b erst einzeln addiert werden:
builder.append(new StringBuilder(a).append(b));
der StringBuilder new StringBuilder(a) ist initial a.length() + 16 lang,
wenn ein 17-Zeichen langer String b addiert wird, muss intern das char-Array vergrößert werden,
bei builder.append(a).append(b); wäre das nicht so