Warum ist das Drucken von "B" in Java langsamer als "#"? Analyse mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum bremst "B" und "#" fliegt? Die Lösung der kultigen Java-Frage

\n\n

Wenn Sie jemals einfache Schleifen in Java geschrieben haben, um Zeichen auf der Konsole auszugeben, ist Ihnen vielleicht eine Seltsamkeit aufgefallen: Die Ausgabe des Kleinbuchstabens 'B' kann merklich langsamer sein als die Ausgabe des Rautezeichens '#'. Dieser Effekt hat einst Stack Overflow erschüttert – die Frage erhielt tausende Aufrufe und Stimmen. Heute analysieren wir, was hinter diesem Phänomen steckt und wie es mit den internen Mechanismen von Java, Konsolen-Ein-/Ausgabe und Pufferung zusammenhängt.

\n\n

Aufbau des Experiments

\n

Stellen Sie sich vor, wir starten zwei Schleifen, die jeweils 100.000 Zeichen auf der Konsole ausgeben: Eine Schleife gibt '#' aus, die andere 'B'. Auf den ersten Blick ist die Operation identisch. Auf vielen Systemen (insbesondere Windows) endet die erste Schleife jedoch nach ~100 ms, die zweite nach mehreren Sekunden. Warum?

\n

Lassen Sie uns dies in Java nachstellen.

\n\n

Code für den Test

\n
public class SlowPrintTest {\n    public static void main(String[] args) {\n        long start = System.nanoTime();\n        for (int i = 0; i < 100_000; i++) {\n            System.out.print("#");\n        }\n        long end = System.nanoTime();\n        System.out.println("\\nZeit für '#': " + (end - start) / 1_000_000 + " ms");\n\n        start = System.nanoTime();\n        for (int i = 0; i < 100_000; i++) {\n            System.out.print("B");\n        }\n        end = System.nanoTime();\n        System.out.println("\\nZeit für 'B': " + (end - start) / 1_000_000 + " ms");\n    }\n}
\n\n

Führen Sie diesen Code im Terminal aus, nicht in der IDE (z. B. über java SlowPrintTest). Das Ergebnis wird Sie überraschen. Unter Windows mit CMD kann der Unterschied das 10- bis 50-fache betragen.

\n\n

Warum passiert das? Die Wurzel des Übels liegt im Schriftart-Rendering

\n

Es liegt nicht an System.out.print() selbst oder an der JVM. Der Hauptgrund liegt darin, wie das Betriebssystem und das Terminal die Ausgabe von Zeichen verarbeiten. System.out in Java ist ein Wrapper um den Standardausgabestrom (stdout). Die Daten werden an die Konsole übergeben, aber die Geschwindigkeit der Bildschirmausgabe hängt ab von:

\n
    \n
  • Zeichenbreite – viele ältere Konsolen (z. B. CMD unter Windows) verwenden nichtproportionale Schriftarten, aber das Zeichen 'B' erfordert möglicherweise ein komplexeres Rendering als '#'.
  • \n
  • Bildschirmaktualisierung – die Konsole kann bei der Ausgabe jedes Zeichens eine vollständige Neuzeichnung der Zeile durchführen, anstatt es einfach hinzuzufügen.
  • \n
  • Terminal-Pufferung – einige Terminals puffern die Ausgabe in Blöcken, aber wenn ein Zeichen die Berechnung der Glyphenbreite erfordert, verlangsamt dies den Vorgang.
  • \n
\n\n

Am deutlichsten zeigt sich der Effekt in der Windows-Eingabeaufforderung (CMD) mit Bitmap-Schriftarten. Wenn Sie 'B' drucken, versucht die Konsole, das Zeichen im Grafikmodus darzustellen, während sie für '#' einen vereinfachten Ansatz verwendet. In modernen Terminals (Windows Terminal, iTerm2, Alacritty) kann der Unterschied weniger auffällig sein oder ganz fehlen.

\n\n

Die Rolle der Pufferung in Java

\n

Ein wichtiger Aspekt der Ausgabeoptimierung ist die Verwendung von gepufferten Strömen. System.out ist standardmäßig nicht gepuffert (genauer gesagt, es ist auf JVM-Ebene gepuffert, aber mit einer kleinen Größe). Jeder Aufruf von print() kann einen Systemaufruf write() auslösen, was sehr teuer ist. Wenn wir eine Pufferung über BufferedWriter hinzufügen, steigt die Leistung, aber der Unterschied zwischen den Zeichen bleibt bestehen – er liegt auf der Seite des Terminals.

\n\n
// Beispiel mit Pufferung\nBufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));\nfor (int i = 0; i < 100_000; i++) {\n    bw.write("#");\n}\nbw.flush();
\n\n

Selbst mit Pufferung erhöht sich die Gesamtzeit, wenn das Terminal 'B' langsam rendert. Ein

Blogs

Buchempfehlungen