Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hallo, wann ist es eigentlich notwendig String-Konkatenierungen "manuell" mit StringBuilder/StringBuffer zu optimieren, wenn man mit einem Java 6 Compiler arbeitet?
Folgender Code wird scheinbar von javac automatisch zu einer Konkatenierung mittels StringBuilder optimiert, wie man z.B. im generierten Byte-Code sehen kann:
Java:
class Test {
public static void main(String[] args) {
String s = "abc";
// ...
s += "def";
}
}
Wann genau kann ich mich auf diese automatische Optimierung verlassen und wann sollte ich sie (an performace-kritischen Stellen) selber ausprogrammieren?
StringBuffer immer dann verwenden, wenn oft concateniert wird, z.B.: in einer Schleife usw.
Wenn ein String einmal initialisiert wird und sich dann nicht mehr ändert, dann kannst du problemlos "String" verwenden.
Folgender Code wird scheinbar von javac automatisch zu einer Konkatenierung mittels StringBuilder optimiert, wie man z.B. im generierten Byte-Code sehen kann
Die Stringkonkatenierung mit + wird immer (wenn nötig) vom Compiler zu einer Konkatenierung mit StringBuilder umgesetzt. Fügt man so Compile-Time-Konstanten zusammen, ist ein Strringbuilder überflüssig und es wird gleich ein fertiger String im Bytecode erzeugt.
Wann genau kann ich mich auf diese automatische Optimierung verlassen und wann sollte ich sie (an performace-kritischen Stellen) selber ausprogrammieren?
optimiert werden kann meiner Ansicht nach eine einzelne Zeile
st += a + b+ c+ d+ e+ f;
bestenfalls mehrere String-Zeilen direkt hintereinander
st += a + b;
st += c+ d;
(auch schon fraglich was bei Exceptions usw. passiert)
unmöglich kann ich mir das für den Paradefall einer Schleife vorstellen:
Java:
for (int i=0; i<10; i++) {
st += ", "+i;
}
hier vor der Schleife einen Builder einzufügen, nach der Schleife den String zu erstellen, das wäre schon sehr aufwendig,
besonders wenn noch anderer Code in der Schleife steht
dass die Zeile für sich optimiert wird ist klar aber eben nicht das entscheidene,
sondern dass 10x ein StringBuilder und jeweils ein String erzeugt wird, in jedem Schleifendurchlauf
richtig optiomal braucht man nur EINEN StringBuilder
Spaßeshalber habe ich mal meinen QuickBench darauf losgelassen:
Java:
public class StringBenches {
public static void main(String[] args) {
QuickBench.benchNxM(10, 50000, new long[] { 1000 },
new long[] { 1000}, new Bench[] {new StringConcatenation_Plus(),
new StringConcatenation_Concat(), new StringConcatenation_StringBuffer(),
new StringConcatenation_InnerStringBuffer(),
new StringConcatenation_StringBuilder(),
new StringConcatenation_InnerStringBuilder()});
}
/* einfaches s += ... */
static class StringConcatenation_Plus implements Bench {
public StringConcatenation_Plus() {
super();
}
@Override
public String getName() {
return "String: Plus";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
String s = "";
for (int i = 0; i < lm; i++) {
s += i;
}
}
};
/* s = s.concat(...) */
static class StringConcatenation_Concat implements Bench {
public StringConcatenation_Concat() {
super();
}
@Override
public String getName() {
return "String: Concat";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
String s = "";
for (int i = 0; i < lm; i++) {
s = s.concat(String.valueOf(i));
}
}
};
/* StringBuffer ausserhalb der Schleife */
static class StringConcatenation_StringBuffer implements Bench {
public StringConcatenation_StringBuffer() {
super();
}
@Override
public String getName() {
return "String: StringBuffer";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
StringBuffer s = new StringBuffer();
for (int i = 0; i < lm; i++) {
s.append(String.valueOf(i));
}
}
};
/* "optimiertes" Beispiel aus dem Thread */
static class StringConcatenation_InnerStringBuffer implements Bench {
public StringConcatenation_InnerStringBuffer() {
super();
}
@Override
public String getName() {
return "String: In-StringBuffer";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
String s = "";
for (int i = 0; i < lm; i++) {
s = new StringBuffer(String.valueOf(s)).append(String.valueOf(i)).toString();
}
}
};
/* StringBuilder ausserhalb der Schleife */
static class StringConcatenation_StringBuilder implements Bench {
public StringConcatenation_StringBuilder() {
super();
}
@Override
public String getName() {
return "String: StringBuilder";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < lm; i++) {
s.append(String.valueOf(i));
}
}
};
/* "optimiertes" Beispiel aus dem Thread */
static class StringConcatenation_InnerStringBuilder implements Bench {
public StringConcatenation_InnerStringBuilder() {
super();
}
@Override
public String getName() {
return "String: In-StringBuilder";
}
@Override
public void reset() {
}
@Override
public void prepare(long lm) {
}
@Override
public void execute(long lm) {
String s = "";
for (int i = 0; i < lm; i++) {
s = new StringBuilder(String.valueOf(s)).append(String.valueOf(i)).toString();
}
}
};
}
Es werden jeweils 1000 mal die Zahlen von 0 bis 999 angehängt.
String: Plus *** ein einfaches konkatenieren durch +=
String: Concat *** ein konkatenieren durch die concat()-Methode
String: StringBuffer *** ein konkatenieren mittels EINEM StringBuffer und der append()-Methode
String: In-StringBuffer *** ein konkatenieren mittels mehreren StringBuffer und der append()-Methode (wie aus dem Beispiel)
String: StringBuilder *** ein konkatenieren mittels EINEM StringBuilder und der append()-Methode
String: In-StringBuilder *** ein konkatenieren mittels mehreren StringBuildern und der append()-Methode (wie aus dem Beispiel)
Summa summarum, wer hätte sich das denken können? Die Fassungen mit einem StringBuffer und einem StringBuilder sind natürlich die "schnellsten". Die Zahlen sind aber allerdings mit Vorsicht zu genießen,wegen Micro-Benchmarking und so weiter... Ich würde jetzt auch nicht raten, wenige Strings, wie in dem Beispiel zum Anfang, jetzt immer mit einem StringBuilder zu verbinden, es kommt halt auf den Fall an.
Nur zeigt das Dekompilat halt aber auch, das die automatischen Optimierungen des Kompilers nicht immer der bessere Weg sein müssen.
Tipp zum Code:
selbst wenn Bench ein nötiges Interface ist, kann man immer noch eine Adapter-Klasse mit leeren Methoden erstellen
damit dann die eigentlichen Tests nicht diese ständig unnötig wiederholen müssen
ein Konstruktor, der nur super() aufruft, ist auch fraglich,
und wo ich grad dabei bei:
getName() könnte im Adapter einfach den Klassenname zurückgeben bisschen lang allerdings