StringBuilder vs StringBuffer: Was wählen und warum ist das wichtig?
\n\nWenn Sie jemals Java-Code geschrieben haben, der intensiv mit Strings arbeitet (z. B. SQL-Abfragen erstellt, JSON formt oder einfach Dutzende von Textfragmenten zusammensetzt), sind Sie sicherlich auf das Dilemma gestoßen: StringBuilder oder StringBuffer? Auf Stack Overflow hat diese Frage Hunderttausende von Aufrufen gesammelt, und das nicht ohne Grund. Auf den ersten Blick sind die Klassen wie Zwillinge – beide erweitern AbstractStringBuilder, beide verwenden ein veränderliches Zeichen-Array, beide können append(), insert(), delete(). Aber der Teufel steckt, wie immer, im Detail.
In diesem Artikel werden wir die Unterschiede nicht nur auflisten. Wir werden sie anhand realer Beispiele analysieren, unter die Haube der JVM schauen und Fragen beantworten, die Ihnen in einem Vorstellungsgespräch garantiert gestellt werden. Los geht's!
\n\n1. Der Hauptunterschied: Synchronisation
\n\nDies ist der wichtigste Punkt, um den sich eigentlich alles dreht. StringBuffer ist threadsicher (thread-safe). Fast alle seine öffentlichen Methoden sind mit dem Schlüsselwort synchronized synchronisiert. StringBuilder ist nicht synchronisiert. Das ist alles.
Schauen wir uns den Code an (vereinfachte Version aus dem echten JDK):
\n\n// StringBuffer (Java 17)\npublic synchronized StringBuffer append(String str) {\n toStringCache = null;\n super.append(str);\n return this;\n}\n\n// StringBuilder\npublic StringBuilder append(String str) {\n super.append(str);\n return this;\n}\n\n\nEine einzige Zeile synchronized – und der ganze Unterschied. Aber diese Zeile ändert alles: Leistung, Anwendungsbereich, sogar die Architektur der Anwendung.
Wann StringBuffer verwenden?
\n- \n
- Sie schreiben eine Multithread-Anwendung. \n
- Dieselbe Instanz von
StringBufferwird von mehreren Threads ohne externe Synchronisation verwendet. \n - Beispiel: Ein Logger, der Nachrichten aus verschiedenen Threads in einem Puffer sammelt. \n
Wann StringBuilder verwenden?
\n- \n
- Singlethread-Code (90 % der Fälle). \n
- Lokale Variablen innerhalb einer Methode. \n
- Schleifen mit String-Verkettung (der Compiler schlägt selbst StringBuilder vor). \n
2. Leistung: Wer ist schneller?
\n\nHier ist alles vorhersehbar: StringBuilder ist schneller als StringBuffer. Wie viel? Abhängig von der Last, aber im Durchschnitt 2-5 Mal. Lassen Sie uns das mit einem einfachen Benchmark überprüfen.
\n\npublic class PerformanceTest {\n private static final int ITERATIONS = 10_000_000;\n\n public static void main(String[] args) {\n // StringBuilder\n long start = System.nanoTime();\n StringBuilder sb = new StringBuilder();\n for (int i = 0; i < ITERATIONS; i++) {\n sb.append("a");\n }\n long sbTime = System.nanoTime() - start;\n\n // StringBuffer\n start = System.nanoTime();\n StringBuffer sbf = new StringBuffer();\n for (int i = 0; i < ITERATIONS; i++) {\n sbf.append("a");\n }\n long sbfTime = System.nanoTime() - start;\n\n System.out.println("StringBuilder: " + sbTime / 1_000_000 + " ms");\n System.out.println("StringBuffer: " + sbfTime / 1_000_000 + " ms");\n System.out.println("Buffer ist " + (sbfTime / (double) sbTime) + " Mal langsamer");\n }\n}\n\n\nAuf meinem Laptop (Intel i7, 16GB RAM, Java 17) ist das Ergebnis stabil: StringBuilder ~180 ms, StringBuffer ~620 ms. Der Unterschied beträgt fast das 3,5-fache. Und wenn man echte Arbeit hinzufügt (Parsing, Formatierung), kann die Lücke noch größer werden. Warum? Weil jeder Aufruf von synchronized eine Monitorprüfung, das Erfassen der Sperre und deren Freigabe bedeutet. In Singlethread-Code ist das reine Zeitverschwendung.
Blogs
Buchempfehlungen
Lesen Sie auch
Wie man in 30 Minuten eine REST API in Rust schreibt: Schritt-für-Schritt-Anleitung