Ich habe gerade einige Sql-Abfrage bauen wie diese in meinem Projekt gefunden:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
Erreicht dieser StringBuilder
sein Ziel, d.h. die Verringerung der Speichernutzung?
Das bezweifle ich, denn im Konstruktor wird der '+' (String-Konkatenoperator) verwendet. Wird das die gleiche Menge an Speicher beanspruchen wie die Verwendung von String wie der unten stehende Code? s ich verstanden habe, ist es anders, wenn StringBuilder.append()
verwendet wird.
return "select id1, " + " id2 " + " from " + " table";
Sind beide Anweisungen gleich im Speicherverbrauch oder nicht? Bitte klären Sie das.
Vielen Dank im Voraus!
Bearbeiten:
BTW, es ist nicht mein Code. Habe ihn in einem alten Projekt gefunden. Außerdem ist die Abfrage nicht so klein wie die in meinem Beispiel :)
Das Ziel der Verwendung von StringBuilder, d.h. die Verringerung des Speichers. Wird es erreicht?
Nein, ganz und gar nicht. Dieser Code verwendet den StringBuilder nicht korrekt. (Ich glaube, Sie haben ihn falsch zitiert, obwohl; sicherlich gibt es keine Anführungszeichen um id2
und table
?)
Beachten Sie, dass das Ziel (normalerweise) darin besteht, den Speicherverbrauch zu reduzieren und nicht den Gesamtspeicherverbrauch, um dem Garbage Collector das Leben zu erleichtern.
Wird das den gleichen Speicherplatz beanspruchen wie die Verwendung von String wie unten?
Nein, es wird mehr Speicherverbrauch verursachen als die von Ihnen zitierte direkte Verknüpfung. (Bis der JVM-Optimierer erkennt, dass der explizite StringBuilder
im Code unnötig ist und ihn herausoptimiert, wenn er kann).
Wenn der Autor dieses Codes StringBuilder
verwenden will (es gibt Argumente dafür, aber auch dagegen; siehe Anmerkung am Ende dieser Antwort), ist es besser, es richtig zu machen (hier nehme ich an, dass es tatsächlich keine Anführungszeichen um id2
und table
gibt):
StringBuilder sb = new StringBuilder(some_appropriate_size);
sb.append("select id1, ");
sb.append(id2);
sb.append(" from ");
sb.append(table);
return sb.toString();
Beachten Sie, dass ich some_appropriate_size
im StringBuilder
Konstruktor aufgeführt habe, so dass er mit genügend Kapazität für den gesamten Inhalt, den wir anhängen wollen, startet. Die Standardgröße, die verwendet wird, wenn man keine angibt, ist 16 Zeichen, was normalerweise zu klein ist und dazu führt, dass der StringBuilder
Neuzuweisungen vornehmen muss, um sich selbst zu vergrößern (IIRC, im Sun/Oracle JDK verdoppelt er sich selbst [oder mehr, wenn er weiß, dass er mehr braucht, um ein bestimmtes append
zu erfüllen], jedes Mal, wenn er keinen Platz mehr hat).
Sie haben vielleicht gehört, dass die String-Verkettung unter der Haube einen StringBuilder
verwendet, wenn sie mit dem Sun/Oracle-Compiler kompiliert wurde. Das ist richtig, es wird ein StringBuilder
für den gesamten Ausdruck verwendet. Aber er wird den Standardkonstruktor verwenden, was in den meisten Fällen bedeutet, dass er eine Neuzuweisung vornehmen muss. Es ist aber einfacher zu lesen. Beachten Sie, dass dies nicht für eine Reihe von Verkettungen gilt. So wird zum Beispiel ein StringBuilder
verwendet:
return "prefix " + variable1 + " middle " + variable2 + " end";
Das bedeutet in etwa:
StringBuilder tmp = new StringBuilder(); // Using default 16 character size
tmp.append("prefix ");
tmp.append(variable1);
tmp.append(" middle ");
tmp.append(variable2);
tmp.append(" end");
return tmp.toString();
Das ist in Ordnung, auch wenn der Standardkonstruktor und die anschließende(n) Neuzuweisung(en) nicht ideal sind, so sind die Chancen doch gut genug — und die Verkettung ist um einiges lesbarer.
Aber das gilt nur für einen einzigen Ausdruck. Mehrere StringBuilder
werden dafür verwendet:
String s;
s = "prefix ";
s += variable1;
s += " middle ";
s += variable2;
s += " end";
return s;
Daraus wird dann so etwas wie das hier:
String s;
StringBuilder tmp;
s = "prefix ";
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable1);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" middle ");
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable2);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" end");
s = tmp.toString();
return s;
...was ziemlich hässlich ist.
Es ist wichtig, sich daran zu erinnern, dass es in allen bis auf sehr wenige Fälle keine Rolle spielt und dass die Lesbarkeit (die die Wartbarkeit verbessert) bevorzugt wird, es sei denn, es gibt ein spezifisches Leistungsproblem.
Wenn Sie bereits alle Teile haben, die Sie anfügen möchten, ist es sinnlos, den StringBuilder überhaupt zu verwenden. Die Verwendung von StringBuilder
und String-Verkettung im selben Aufruf wie in Ihrem Beispielcode ist sogar noch schlimmer.
Dies wäre besser:
return "select id1, " + " id2 " + " from " + " table";
In diesem Fall findet die String-Verkettung ohnehin zur Compile-Time statt, ist also äquivalent zum Even-Simpler:
return "select id1, id2 from table";
Die Verwendung von new StringBuilder().append("select id1, ").append(" id2 ")....toString()
wird in diesem Fall tatsächlich die Leistung beeinträchtigen, da die Verkettung zur Ausführungszeit und nicht zur Kompilierzeit durchgeführt wird. Ups.
Wenn der eigentliche Code eine SQL-Abfrage erstellt, indem er Werte in die Abfrage aufnimmt, dann ist das ein weiteres eigenes Problem, nämlich dass Sie parametrisierte Abfragen verwenden sollten, bei denen die Werte in den Parametern und nicht in der SQL angegeben werden.
Ich habe einen Artikel über String
/ StringBuffer
, den ich vor einer Weile geschrieben habe - bevor StringBuilder
aufkam. Die Prinzipien gelten aber für StringBuilder
in gleicher Weise.
In dem Code, den Sie gepostet haben, gäbe es keine Vorteile, da Sie den StringBuilder falsch verwenden. Sie erstellen in beiden Fällen denselben String. Mit dem StringBuilder können Sie die +
Operation auf Strings vermeiden, indem Sie die append
Methode verwenden.
Sie sollten sie auf diese Weise verwenden:
return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
In Java ist der String-Typ eine unveränderliche Folge von Zeichen. Wenn Sie also zwei Strings addieren, erzeugt die VM einen neuen String-Wert, bei dem beide Operanden verkettet sind.
Der StringBuilder bietet eine veränderliche Folge von Zeichen, die Sie verwenden können, um verschiedene Werte oder Variablen zu verketten, ohne neue String-Objekte zu erstellen, und kann daher manchmal effizienter sein als die Arbeit mit Strings
Dies bietet einige nützliche Funktionen, wie das Ändern des Inhalts einer Zeichensequenz, die als Parameter innerhalb einer anderen Methode übergeben wird, was mit Strings nicht möglich ist.
private void addWhereClause(StringBuilder sql, String column, String value) {
//WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection
sql.append(" where ").append(column).append(" = ").append(value);
}
Mehr Informationen unter http://docs.oracle.com/javase/tutorial/java/data/buffers.html