Man liest öfters dass die Verwendung von += auf Stringobjekte in Schleifen zu schlechter Performance führt. Abhilfe soll das Verwenden von StringBuffer (oder Builder) schaffen.
Außerhalb von Schleifen wäre das nicht nötig, da der Compiler sowieso das += zu einem äquivalenten StringBuffer.append() umwandelt. Nur in Schleifen soll das nicht so gut funktionieren, weshalb man da eben direkt mit StringBuffer arbeiten soll.
Gilt das immer noch? Oder kann der Compiler mittlerweile auch mit dynamisch veränderten Strings gut umgehen?
Ob das relevant ist lässt aber cniht sagen ohne zu messen, wenn diese Schleife zB. nur 10 mal durchlaufen wird, kann es egal sein ob StringBuilder(!!! StringBuffer ist langsamer) oder +
In den Schleifen wird += auch mit dem StringBuilder gemacht, nur da wird der StringBuilder jedes mal aufs neue initialisiert:
Java:
String s ="";for(int i=0;i<20;i++){
s+="a";}
Das könnte man auch manuell als
Java:
String s ="";StringBuilder sb =newStringBuilder(s);for(int i=0;i<20;i++){
sb.append("a");}
s = sb.toString();
Aber der Compiler macht daraus:
Java:
String s ="";for(int i=0;i<20;i++){StringBuilder sb =newStringBuilder(s);
sb.append("a");
s = sb.toString();}
Es kommt eben drauf an was in der Schleife passiert und wie oft sie durchgeführt wird. Meistens kann man ganz andere und sinnvollere Sachen optimieren, bevor man sich mit sowas beschäftigt
definitiv, bei großen Strings. Also anängen von kurzen Texten an langen Log Texte usw.... das bremst...
Java:
publicclassTest{publicstaticvoidmain(String[] args){int anzahl =5000;long t =System.currentTimeMillis();String text ="Man liest öfters dass die Verwendung von += auf Stringobjekte in Schleifen zu schlechter Performance führt. Abhilfe soll das Verwenden von StringBuffer (oder Builder) schaffen."+"Außerhalb von Schleifen wäre das nicht nötig, da der Compiler sowieso das += zu einem äquivalenten StringBuffer.append() umwandelt. Nur in Schleifen soll das nicht so gut funktionieren, weshalb man da eben direkt mit StringBuffer arbeiten soll."+"+Gilt das immer noch? Oder kann der Compiler mittlerweile auch mit dynamisch veränderten Strings gut umgehen? ";String result ="";for(int i =0; i < anzahl; i++){if(i %100==0)System.out.print(".");
result += text;}System.out.println("t1: "+(System.currentTimeMillis()- t));
t =System.currentTimeMillis();StringBuffer result2 =newStringBuffer();for(int i =0; i < anzahl; i++){if(i %100==0)System.out.print(".");
result2.append(text);}System.out.println("t2: "+(System.currentTimeMillis()- t));}}
Die Gründe dafür sind mir ein Rätsel. Compiler können doch schon lange sehr komplexe Optimierungen vornehmen; warum wird Java-Anfängern so ein Stolperstein in den Weg geworfen? Das sollte doch wirklich einfach optimierbar sein. Kennt jemand die Gründe dafür? Ändert sich da mit Java 7 etwas? (Ich bin aktuell noch auf Java 6; im OpenJDK 1.6.0_22 wird der [c]StringBuilder[/c] noch innerhalb der Schleife erzeugt.)
Strings sind imutable, das hat eine wichtige bedeutung bei der art wir referenzen geändert werden können und übergeben werden, wenn jetzt der kompiler nicht weiß wass der programmierer beabsichtigt, kann man damit vieles killen wenn man sowas nachträglich ändert.
Strings sind imutable, das hat eine wichtige bedeutung bei der art wir referenzen geändert werden können und übergeben werden, wenn jetzt der kompiler nicht weiß wass der programmierer beabsichtigt, kann man damit vieles killen
Ok, das erklärt wieso die scheinbar naheliegende Optimierung mit einem neuen StringBuilder-Objekt und append() in der Schleife nicht einfach standardmäßig durchgeführt werden kann. Wenn mir jetzt auch kein Fall einfällt, bei dem es Probleme geben könnte.
Wieso sollte das für einen Anfänger ein Stolperstein bzw. überhaupt relevant sein?
Wenn ein harmlos wirkender Operator im Hintergrund einen umfangreichen Prozess auslöst, ist das nicht wirklich optimal. Stolperstein ist vielleicht übertrieben, aber ich könnte mir schon vorstellen dass das bei einigen zu eher langsamen Programmen geführt hat.
Was mir an der Stringbehandlung sowieso nicht gefällt:
s1 und s2 verweisen nicht aufs gleiche Objekt. Das heißt für mich der Compiler macht aus [c]String s2 = "String";[/c] ein [c]String s2 = new String( "String" );[/c]. s2 und s3 sind dann aber wieder das gleiche Objekt. Für mich wenig intuitiv.
Ich habe das Gefühl man wollte die Stringbehandlung ähnlich gestalten wie in nicht-OOP und dabei kam dann diese Konstruktion heraus. Und das blockiert dann vielleicht die effektive Optimierung von += in Schleifen?
s1 und s2 verweisen nicht aufs gleiche Objekt. Das heißt für mich der Compiler macht aus [c]String s2 = "String";[/c] ein [c]String s2 = new String( "String" );[/c]. s2 und s3 sind dann aber wieder das gleiche Objekt. Für mich wenig intuitiv.
Wie kommst du denn zu dem Entschluss? Ich denke, dass es genauso bleibt, wie es da steht. s1 ist eine Kopie von "String" aus dem Konstantenpool. Die Konstante "String" existiert ja schon, bevor du den Konstruktor aufrufst. s2 und s3 verweisen dann nur noch auf diese Konstante.
Bei s1 ist das noch verständlich, ich sage ja extra mit new dass ein neues Objekt angelegt werden soll. s2 referenziert dann aber nicht einfach das s1-Objekt, sondern es wird im Pool ein neues Objekt "String" angelegt. Bei s3 erkennt der Compiler dass "String" schon im Pool ist und referenziert das Objekt dann einfach, daher s2==s3.
Es gibt anscheinend zwei Bereiche für String-Objekte. Einmal solche die mit new angelegt wurden und dann die mit = "String" angelegten, die in den Pool wandern, bzw einen dort bereits existierenden String referenzieren. Anders kann ich mir nicht erklären dass s1 != s2. Und das ist es was ich umständlich und nicht passend zum sonstigen Objekt-System finde.