# StringBuilder notwendig ab wann?



## KLeines (3. Mrz 2008)

Kuckuck,  :lol: 

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?


----------



## Leroy42 (3. Mrz 2008)

Wenn du das in einer Schleife ca. 1 Milliarde mal machst,
kann dir das schonmal eine ganze Sekunde einsparen!


----------



## Marco13 (3. Mrz 2008)

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....


----------



## KLeines (3. Mrz 2008)

Leroy42 hat gesagt.:
			
		

> Wenn du das in einer Schleife ca. 1 Milliarde mal machst,
> kann dir das schonmal eine ganze Sekunde einsparen!


 echt soviel? also eine milliarde?


----------



## tfa (3. Mrz 2008)

Marco13 hat gesagt.:
			
		

> Hm... @Leroy42 der Unterschied zwischen einer String-Addition und einem StringBuilder kann schon deutlicher sein, als man das im ersten Moment vermuten würde...


Und in welcher Weise?
String-Konkatenation ("Addition") ist ja nichts weiter als die Verwendung eines StringBuilders.


----------



## maki (3. Mrz 2008)

In Schleifen Stringbuiler verwenden, ansonsten +

So ungefähr..


----------



## Wolfgang Lenhard (3. Mrz 2008)

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.


 Javainsel


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.


----------



## Janus (4. Mrz 2008)

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:

```
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.


----------



## Wolfgang Lenhard (4. Mrz 2008)

... 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.


----------



## maki (4. Mrz 2008)

Janus,

die zweite Schleife ist quatsch, genauso wie die erste 
Kein Mensch schreibt das so ist, auch ziemlich sinnfrei, oder?

Tatsache ist das je nach Situation entweder StringBuilder oder + schneller sind.


----------



## Murray (4. Mrz 2008)

maki hat gesagt.:
			
		

> Tatsache ist das je nach Situation entweder StringBuilder oder + schneller sind.


In welcher Situation ist die +-Konkatenation denn schneller als ein StringBuilder? Ich dachte, der Compiler würde die +-Konkatenation ohnehin auf einen StringBuilder abbilden.


----------



## maki (4. Mrz 2008)

Je nachdem Murray,

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:

```
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
> ...



Im Gegensatz dazu dasselbe nochmal mi der Server VM:


> java -server foo.PerfTest
> 
> For-Schleife mit String:
> 17666163926
> ...


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.

Hier noch ein sehr interessanter Artikel zum Thema der versuchten Optimierungen durch Programmierer und warum man das besser sein lässt bevor man genau weiss warum: 
http://java.sun.com/developer/technicalArticles/Interviews/community/kabutz_qa.html


----------



## Murray (4. Mrz 2008)

Eben mal ausprobiert:

Diese Klasse

```
public class Concat{
	
	
	public String t1( String s1, String s2) {
		 return s1 + s2;
  }

	public String t2( String s1, String s2) {
		 return new StringBuilder().append( s1).append( s2).toString();
  }
  
}
```

wird mit dem JDK 1.6 zu folgendem Bytecode:

```
Compiled from "Concat.java"
public class Concat extends java.lang.Object{
public Concat();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	return

public java.lang.String t1(java.lang.String, java.lang.String);
  Code:
   0:	new	#2; //class java/lang/StringBuilder
   3:	dup
   4:	invokespecial	#3; //Method java/lang/StringBuilder."<init>":()V
   7:	aload_1
   8:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:	aload_2
   12:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:	invokevirtual	#5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18:	areturn

public java.lang.String t2(java.lang.String, java.lang.String);
  Code:
   0:	new	#2; //class java/lang/StringBuilder
   3:	dup
   4:	invokespecial	#3; //Method java/lang/StringBuilder."<init>":()V
   7:	aload_1
   8:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:	aload_2
   12:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:	invokevirtual	#5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18:	areturn

}
```

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:

```
public String t3( String s1, String s2) {
		 return new StringBuilder( s1.length() + s2.length()).append( s1).append( s2).toString();
  }
```

Als Bytecode dann

```
public java.lang.String t3(java.lang.String, java.lang.String);
  Code:
   0:	new	#2; //class java/lang/StringBuilder
   3:	dup
   4:	aload_1
   5:	invokevirtual	#6; //Method java/lang/String.length:()I
   8:	aload_2
   9:	invokevirtual	#6; //Method java/lang/String.length:()I
   12:	iadd
   13:	invokespecial	#7; //Method java/lang/StringBuilder."<init>":(I)V
   16:	aload_1
   17:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   20:	aload_2
   21:	invokevirtual	#4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24:	invokevirtual	#5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:	areturn
```
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.


----------



## SlaterB (4. Mrz 2008)

@maki

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


----------



## maki (4. Mrz 2008)

> Es wird also in beiden Fällen der gleiche Code erzeugt; die Verwendung des StringBuilders bringt zwar keinen Vorteil, aber definitiv auch keinen Nachteil.


Ja, in Methoden ersetzt der Compiler durch StringBuilder (im Moment )

Nachteile des StringBuilders: Lesbarkeit (!), kann nicht mehr weiter durch zukünftige Compiler Versionen optimiert werden (siehe Link)

In Schleifen ist das anders, da kann der Compiler den StringBiulder nicht ersetzen.


----------



## The_S (4. Mrz 2008)

Mal ne blöde Frage, wie schaut ihr euch den generierten Bytecode an ???:L ?


----------



## maki (4. Mrz 2008)

> @maki
> 
> du solltest fair spielen, z.B.


Ja SlaterB, es gibt noch mehr Punkte wo mein subjektiver Microbenchmark unfair ist


----------



## Murray (4. Mrz 2008)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Mal ne blöde Frage, wie schaut ihr euch den generierten Bytecode an ???:L ?


javap -c <Klassenname>


----------



## The_S (4. Mrz 2008)

ah ... danke


----------



## tfa (4. Mrz 2008)

Hobbit_Im_Blutrausch hat gesagt.:
			
		

> Mal ne blöde Frage, wie schaut ihr euch den generierten Bytecode an ???:L ?


In Eclipse class-Datei auswählen, rechte Maustaste->Open with Classfile Viewer


----------



## ARadauer (4. Mrz 2008)

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?


----------



## maki (4. Mrz 2008)

> 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.


Je nachdem, in Schleifen schon, ansonsten nicht.



> btw: ich benutz immer StringBuffer, gibts da nennenswerte unterschiede?


StringBuffer is threadsicher und damit langsamer.
Am besten du gewöhnst dir das ab, ohne Schleife ist + besser


----------



## The_S (4. Mrz 2008)

maki hat gesagt.:
			
		

> > btw: ich benutz immer StringBuffer, gibts da nennenswerte unterschiede?
> 
> 
> StringBuffer is threadsicher und damit langsamer.
> Am besten du gewöhnst dir das ab, ohne Schleife ist + besser



und StringBuilder gibt es erst seit 1.5


----------



## Murray (4. Mrz 2008)

ARadauer hat gesagt.:
			
		

> 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.


----------



## Saxony (4. Mrz 2008)

Hiho,

hier mal ein Beispiel des Rückgabewertes der paramString-Methode von java.awt.event.MouseWheelEvent:

Original Klasse:


```
return super.paramString()+",scrollType="+scrollTypeStr+
         ",scrollAmount="+getScrollAmount()+",wheelRotation="+
         getWheelRotation();
```

Dekompiliert mit JAD ergibt sich folgendes:


```
return (new StringBuilder()).append(super.paramString()).append(
				",scrollType=").append(s).append(",scrollAmount=").append(
				getScrollAmount()).append(",wheelRotation=").append(
				getWheelRotation()).toString();
```

Man kann also davon ausgehen, dass der Compiler(ab 1.5) Stringadditionen intern in StringBuilder-Appends umstrickt.

bye Saxony


----------



## tfa (4. Mrz 2008)

Saxony hat gesagt.:
			
		

> Man kann also davon ausgehen, dass der Compiler(ab 1.5) Stringadditionen intern in StringBuilder-Appends umstrickt.


Das hat der schon immer so gemacht -  nicht nur ab 1.5


----------



## SlaterB (4. Mrz 2008)

hehe, vor 1.5 ganz bestimmt nicht mit StringBuilder


----------



## Saxony (4. Mrz 2008)

Möchte ich bezweifeln das der Compiler vor 1.5 aus Stringadditionen StringBuilder-Appends erzeugt hat. 

bye Saxony


----------



## Saxony (4. Mrz 2008)

Gleiche Idee SlaterB.


----------



## SlaterB (4. Mrz 2008)

gleiche Idee, bessere Geschwindigkeit, quasi ein SlaterBuilder-Post


----------



## tfa (4. Mrz 2008)

Ups, sry. Davor war's natürlich der StringBuffer.


----------



## KLeines (4. Mrz 2008)

SlaterB hat gesagt.:
			
		

> hehe, vor 1.5 ganz bestimmt nicht mit StringBuilder



also wenn ich java 1.6 benutze brauche ich StringBuilder gar nicht explizit benutzen? Wird ja intern eh umgewandelt nicht?


----------



## SlaterB (4. Mrz 2008)

StringBuffer in 1.4 ist auch nur 0.001% langsamer, 
du hast also bei normalen Compilern in jedem Fall keine Probleme bei String-Addition in einem Zug,

nur bei
for-Schleife {
string += andererString;
}

ist die String-Addition etwas gefährlich, das ist nicht so leicht zu optimieren


----------



## PELLE (4. Mrz 2008)

SlaterB hat gesagt.:
			
		

> StringBuffer in 1.4 ist auch nur 0.001% langsamer,
> du hast also bei normalen Compilern in jedem Fall keine Probleme bei String-Addition in einem Zug,
> 
> nur bei
> ...


 das interessiert mich jetzt auch...:

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?


```
public StringBuilder generateSpace(int number)
	{
		StringBuilder space = new StringBuilder();
		
		for (int i = 0 ; i < number; i++)
        {
          space.append(' ');
        } 
		return space; 
	}
```


----------



## SlaterB (4. Mrz 2008)

eine derartige Optimierung halte ich für ausgeschlossen, da zu komplex,
kann aber nur meine Meinung ohne Wissen zum Besten geben 

die bisherigen Pseudo-Tests dazu sind ja auch eindeutig

----


Rückgabewert StringBuilder statt String wäre in diesem Fall eh was anderes


----------



## Janus (4. Mrz 2008)

maki hat gesagt.:
			
		

> Janus,
> 
> die zweite Schleife ist quatsch, genauso wie die erste
> Kein Mensch schreibt das so ist, auch ziemlich sinnfrei, oder?


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.


----------



## Saxony (5. Mrz 2008)

So Freunde um das mal auch für Schleifen zu klären, habe ich folgenden Code geschrieben:


```
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á:


```
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]

bye Saxony


----------



## SlaterB (5. Mrz 2008)

> 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


----------



## Saxony (5. Mrz 2008)

SlaterB hat gesagt.:
			
		

> 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,



Diese Festellung hab ich explizit für die Frage von PELLE so hingeschrieben.



			
				PELLE hat gesagt.:
			
		

> Wandelt Java ab 1.6 die strings additionen jetzt nicht in einen Stringbuilder um in einer for-schleife?



bye Saxony


----------



## PELLE (5. Mrz 2008)

> Diese Festellung hab ich explizit für die Frage von PELLE so hingeschrieben.



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


----------



## Wolfgang Lenhard (5. Mrz 2008)

Was passiert eigentlich dann bei

```
builder.append("Wird hier zunächst ein "
            + "String konkateniert?");
```


----------



## Guest (5. Mrz 2008)

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 =)


----------



## SlaterB (5. Mrz 2008)

@Wolfgang:
was jeder Compiler/ Interpreter draus macht kann man nicht unbedingt vorhersagen, 
aber ein Beispiel:


```
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


----------

