Ist es besser, String.format anstelle der String-Verkettung in Java zu verwenden?
-
06-09-2019 - |
Frage
Gibt es einen spürbaren Unterschied zwischen der Verwendung? String.format
und String-Verkettung in Java?
Ich neige dazu, zu verwenden String.format
aber gelegentlich rutscht es ab und es kommt zu einer Verkettung.Ich habe mich gefragt, ob einer besser ist als der andere.
Wie ich es sehe, String.format
gibt Ihnen mehr Möglichkeiten beim „Formatieren“ der Zeichenfolge;und die Verkettung bedeutet, dass Sie sich keine Sorgen machen müssen, versehentlich ein zusätzliches %s einzufügen oder eines zu verpassen.
String.format
ist auch kürzer.
Welche besser lesbar ist, hängt davon ab, wie Ihr Kopf funktioniert.
Lösung
Ich würde vorschlagen, dass es besser Praxis String.format()
zu bedienen ist. Der Hauptgrund ist, dass String.format()
kann leichter mit von Ressourcendateien geladen Text lokalisiert werden, während Verkettung nicht ohne Erzeugung eines neuen ausführbaren mit unterschiedlichen Code für jede Sprache lokalisiert werden kann.
Wenn Sie auf Ihre Anwendung ist lokalisierbar Sie auch in die Gewohnheit der Angabe Argument Positionen für Ihre Format-Token als auch bekommen sollte:
"Hello %1$s the time is %2$t"
Dies kann dann den Namen lokalisiert und haben werden und Zeit Token getauscht, ohne eine erneute Kompilierung der ausführbaren Datei zu benötigen für die andere Ordnung zu berücksichtigen. Mit Argument Positionen können Sie auch das gleiche Argument wiederverwenden, ohne sie in die Funktion übergeben zweimal:
String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
Andere Tipps
Über Performance:
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = String.format("Hi %s; Hi to you %s",i, + i*2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
}
Die Zeit Ergebnisse sind wie folgt:
- Verkettungs = 265 ms
- Format = 4141 Millisekunde
Daher Verkettung ist viel schneller als String.format.
Da es eine Diskussion über die Leistung ist, ich dachte ich, in einem Vergleich hinzufügen würde, die Stringbuilder enthalten. Es ist in der Tat schneller als die concat und natürlich die String.format Option.
Um dies eine Art von Äpfel mit Äpfeln vergleichen mache ich einen neuen String in der Schleife instanziiert eher außerhalb als (dies ist tatsächlich schneller als die meisten nur eine Instanziierung tun wahrscheinlich aufgrund der Overhead der Neuzuteilung Platz für den Looping anhängen an das Ende eines Builder).
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
log.info("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
log.info("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("; Hi to you ").append(i * 2);
}
end = System.currentTimeMillis();
log.info("String Builder = " + ((end - start)) + " millisecond");
- 2012-01-11 16: 30: 46.058 INFO [TestMain] - Format = 1416 Millisekunde
- 2012-01-11 16: 30: 46.190 INFO [TestMain] - Verkettungs = 134 ms
- 2012-01-11 16: 30: 46.313 INFO [TestMain] - String Builder = 117 ms
Ein Problem bei .format
ist, dass Sie statische Typsicherheit verlieren. Sie können für Ihr Format zu wenige Argumente haben, und Sie können die falschen Typen für den Formatbezeich haben -. Sowohl zu einem IllegalFormatException
führenden zur Laufzeit , so dass Sie mit der Protokollierung Code, die Produktion bricht am Ende vielleicht
Im Gegensatz dazu können die Argumente +
vom Compiler getestet werden.
Welche ist besser lesbar hängt davon ab, wie Sie Ihren Kopf arbeitet.
Sie haben Ihre Antwort recht.
Es ist eine Frage des persönlichen Geschmacks.
String-Verkettung ist geringfügig schneller, nehme ich an, aber das sollte vernachlässigbar sein.
Hier ist ein Test mit mehreren Probengrößen in Millisekunden.
public class Time {
public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;
public static void main(String[] args) {
int i = 1;
for(int run=1; run <= 12; run++){
for(int test =1; test <= 2 ; test++){
System.out.println(
String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
test(run, i);
}
System.out.println("\n____________________________");
i = i*3;
}
}
public static void test(int run, int iterations){
long start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = "echo " + i + " > "+ sysFile;
}
long t = System.nanoTime() - start;
String r = String.format(" %-13s =%10d %s", "Concatenation",t,"nanosecond");
System.out.println(r) ;
start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = String.format(cmdString, i);
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "Format",t,"nanosecond");
System.out.println(r);
start = System.nanoTime();
for( int i=0;i<iterations; i++){
StringBuilder b = new StringBuilder("echo ");
b.append(i).append(" > ").append(sysFile);
String s = b.toString();
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "StringBuilder",t,"nanosecond");
System.out.println(r);
}
}
TEST: 1, RUN: 1, Iterations: 1
Concatenation = 14911 nanosecond
Format = 45026 nanosecond
StringBuilder = 3509 nanosecond
TEST: 1, RUN: 2, Iterations: 1
Concatenation = 3509 nanosecond
Format = 38594 nanosecond
StringBuilder = 3509 nanosecond
____________________________
TEST: 2, RUN: 1, Iterations: 3
Concatenation = 8479 nanosecond
Format = 94438 nanosecond
StringBuilder = 5263 nanosecond
TEST: 2, RUN: 2, Iterations: 3
Concatenation = 4970 nanosecond
Format = 92976 nanosecond
StringBuilder = 5848 nanosecond
____________________________
TEST: 3, RUN: 1, Iterations: 9
Concatenation = 11403 nanosecond
Format = 287115 nanosecond
StringBuilder = 14326 nanosecond
TEST: 3, RUN: 2, Iterations: 9
Concatenation = 12280 nanosecond
Format = 209051 nanosecond
StringBuilder = 11818 nanosecond
____________________________
TEST: 5, RUN: 1, Iterations: 81
Concatenation = 54383 nanosecond
Format = 1503113 nanosecond
StringBuilder = 40056 nanosecond
TEST: 5, RUN: 2, Iterations: 81
Concatenation = 44149 nanosecond
Format = 1264241 nanosecond
StringBuilder = 34208 nanosecond
____________________________
TEST: 6, RUN: 1, Iterations: 243
Concatenation = 76018 nanosecond
Format = 3210891 nanosecond
StringBuilder = 76603 nanosecond
TEST: 6, RUN: 2, Iterations: 243
Concatenation = 91222 nanosecond
Format = 2716773 nanosecond
StringBuilder = 73972 nanosecond
____________________________
TEST: 8, RUN: 1, Iterations: 2187
Concatenation = 527450 nanosecond
Format = 10291108 nanosecond
StringBuilder = 885027 nanosecond
TEST: 8, RUN: 2, Iterations: 2187
Concatenation = 526865 nanosecond
Format = 6294307 nanosecond
StringBuilder = 591773 nanosecond
____________________________
TEST: 10, RUN: 1, Iterations: 19683
Concatenation = 4592961 nanosecond
Format = 60114307 nanosecond
StringBuilder = 2129387 nanosecond
TEST: 10, RUN: 2, Iterations: 19683
Concatenation = 1850166 nanosecond
Format = 35940524 nanosecond
StringBuilder = 1885544 nanosecond
____________________________
TEST: 12, RUN: 1, Iterations: 177147
Concatenation = 26847286 nanosecond
Format = 126332877 nanosecond
StringBuilder = 17578914 nanosecond
TEST: 12, RUN: 2, Iterations: 177147
Concatenation = 24405056 nanosecond
Format = 129707207 nanosecond
StringBuilder = 12253840 nanosecond
Hier ist der gleiche Test wie oben mit der Änderung des Aufrufs der toString () Methode auf dem String . Die nachstehenden Ergebnisse zeigen, dass der String Ansatz ist nur ein bisschen langsamer als String-Verkettung mit dem + Operator.
Datei: StringTest.java
class StringTest {
public static void main(String[] args) {
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("Hi to you ").append(i * 2).toString();
}
end = System.currentTimeMillis();
System.out.println("String Builder = " + ((end - start)) + " millisecond");
}
}
Shell-Befehle: (kompilieren und StringTest 5 Mal ausgeführt wird)
> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"
Ergebnisse:
Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond
Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
String.format()
ist mehr als nur verketten Saiten. Zum Beispiel können Sie Zahlen in einer bestimmten Gebietsschema mit String.format()
angezeigt werden soll.
Wenn Sie jedoch nicht über die Lokalisierung kümmern, gibt es keinen funktionalen Unterschied. Vielleicht ist das eine ist schneller als die anderen, aber in den meisten Fällen wird es vernachlässigbar sein ..
Im Allgemeinen String-Verkettung sollte über String.format
bevorzugt werden. Letzteres hat zwei wesentliche Nachteile:
- Es ist kodieren nicht die Zeichenfolge in einer lokalen Weise gebaut werden.
- Der Bauprozess wird in einem String codiert werden.
Mit dem Punkt 1, ich meine, dass es nicht möglich ist, zu verstehen, was ein String.format()
Anruf wird in einem einzigen sequentiellen Durchgang zu tun. Man ist gezwungen hin und her zwischen dem Format-String und den Argumenten zu gehen, während die Position der Argumente zählen. Für kurze Verkettungen, dann ist dies ein Problem, nicht viel. In diesen Fällen ist jedoch die String-Verkettung weniger ausführlich.
Mit dem Punkt 2, dann meine ich, dass der wichtige Teil des Bauprozesses in der Format-String codiert wird, (unter Verwendung eines DSL). Strings Code zu vertreten hat viele Nachteile. Es ist nicht von Natur aus typsicher, und kompliziert Syntax-Hervorhebung, Code-Analyse, Optimierung, etc.
Natürlich, wenn Tools oder Frameworks außerhalb der Java-Sprache, neue Faktoren ins Spiel kommen können.
Ich habe keine spezifische Benchmarks getan, aber ich würde denken, dass Verkettung schneller sein kann. String.format () erzeugt ein neues Formatter was wiederum einen neuen String (mit einer Größe von nur 16 Zeichen) erzeugt. Das ist eine ganze Menge Aufwand vor allem, wenn Sie eine längere Zeichenfolge formatieren und String hält mit ändern.
Allerdings Verkettung ist weniger nützlich und schwieriger zu lesen. Wie immer, es lohnt sich, einen Benchmark auf Ihren Code zu tun, um zu sehen, was besser ist. Die Unterschiede in der Server-Anwendung nach dem Ressourcenbündel, Lokale usw. geladen werden im Speicher und der Code ist JITted vernachlässigbar sein.
Vielleicht als Best Practice, wäre es eine gute Idee zu sein, eigenen Formatter zu schaffen, mit einem richtigen Größe Stringbuilder (Appendable) und Locale und verwendet, wenn Sie eine Menge Formatierung zu tun haben.
Es kann ein merklicher Unterschied sein.
String.format
ist recht komplex und verwendet einen regulären Ausdruck unter, so macht es nicht zur Gewohnheit, es überall zu verwenden, aber nur dort, wo Sie sie brauchen.
StringBuilder
wäre eine Größenordnung schneller sein (wie jemand hier bereits darauf hingewiesen).
Sie können nicht vergleichen String Concatenation und String.Format durch das Programm oben.
Sie können versuchen, diese auch die Position werden Vertauschen Ihrer String.Format und Verkettungs im Code-Block wie unten mit
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}
Sie werden überrascht sein, dass Format zu sehen, schneller hier funktioniert. Dies ist, da die intial Objekte erstellt nicht freigegeben werden kann, und es kann ein Problem mit Speicherzuweisung und damit die Leistung sein.
Es dauert ein wenig, sich an String.Format zu gewöhnen, aber es lohnt sich in den meisten Fällen.In der Welt von NRA (niemals etwas wiederholen) ist es äußerst nützlich, Ihre tokenisierten Nachrichten (Protokollierung oder Benutzer) in einer Konstantenbibliothek (ich bevorzuge eine statische Klasse) aufzubewahren und sie bei Bedarf mit String.Format aufzurufen, unabhängig davon, ob Sie lokalisieren oder nicht.Der Versuch, eine solche Bibliothek mit einer Verkettungsmethode zu verwenden, ist bei jedem Ansatz, der eine Verkettung erfordert, schwieriger zu lesen, Fehler zu beheben, Korrektur zu lesen und zu verwalten.Ein Austausch ist eine Option, aber ich bezweifle, dass er leistungsfähig ist.Nach Jahren der Verwendung besteht mein größtes Problem mit String.Format darin, dass der Aufruf unpraktisch lang ist, wenn ich ihn an eine andere Funktion (wie Msg) übergebe, aber das lässt sich mit einer benutzerdefinierten Funktion, die als Alias dient, leicht umgehen .
Ich denke, wir mit MessageFormat.format
gehen können, wie es auf den beiden Lesbarkeit und auch Performance-Aspekte sollte gut sein.
habe ich das gleiche Programm, das man verwendet, von Icaro in seiner obenen Antwort und ich verbessern es mit dem Code angehängt für die Verwendung von MessageFormat
die Performance-Zahlen zu erklären.
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
}
Verkettungs = 69 Millisekunden
Format = 1435 Millisekunde
Message = 200 ms
UPDATES:
Wie pro SonarLint Report, Printf-style-Format-Strings richtig sein sollte (Tintenfisch: S3457)
Da printf-Stil Format-Strings zur Laufzeit interpretiert werden, anstatt durch den Compiler validiert, können sie Fehler enthalten, die in den falschen Saiten führen geschaffen. Diese Regel überprüft statisch die Korrelation von printf-Stil Format-Strings, um ihre Argumente beim Aufruf der Format (...) Methoden der java.util.Formatter, java.lang.String, java.io.PrintStream, Message und java.io .PrintWriter Klassen und die printf (...) Methoden der java.io.PrintStream oder java.io.PrintWriter Klassen.
Ich ersetze den printf-Stil mit den geschweiften Klammern und ich habe wie unten interessante Ergebnisse etwas.
Verkettungs = 69 Millisekunden
Format = 1107 Millisekunde
Format: geschweiften Klammern = 416 Millisekunde
Message = 215 Millisekunden
Message: geschweiften Klammern = 2517 Millisekunde
Mein Fazit:
Wie ich oben hervorgehoben, String.format mit geschweiften Klammern sollte eine gute Wahl sein, um Vorteile der guten Lesbarkeit und auch Leistung.