Question
Why Printing "B" Can Be Slower Than "#" in Java Console Output
Question
In Java, why can printing a 1000 x 1000 matrix containing O and B appear dramatically slower than printing a matrix containing O and #?
Here is the first version:
Random r = new Random();
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
if (r.nextInt(4) == 0) {
System.out.print("O");
} else {
System.out.print("#");
}
}
System.out.println("");
}
This version completed in about 8.52 seconds.
Here is the second version:
Random r = new Random();
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
if (r.nextInt(4) == 0) {
System.out.print("O");
} else {
System.out.print("B");
}
}
System.out.println("");
}
This version took about 259.152 seconds.
The only change is replacing # with B. Why would that cause such a large difference in runtime?
Additional observations:
- Printing only
System.out.print("#");took about7.8871seconds. - Printing only
System.out.print("B");appeared to run much longer. - On online environments such as Ideone, both versions ran at about the same speed.
Test conditions:
- The code was run from NetBeans 7.2
- Output was written to the NetBeans console
- Timing was measured using
System.nanoTime()
Short Answer
By the end of this page, you will understand that the slowdown is usually not caused by Java string printing itself, but by the environment that renders the output, especially an IDE console. You will learn how console rendering, buffering, font drawing, and measurement methods can affect performance, and why benchmark results can be misleading when they include screen output.
Concept
The core concept here is I/O performance versus computation performance.
In the example, Java is not doing much computation:
- generating random numbers
- choosing between two characters
- printing them
The expensive part is the printing.
Why this matters
When you write to System.out, the work does not stop at Java sending characters. The output often goes through several layers:
- Java writes to
PrintStream - the stream may buffer data
- the terminal or IDE console receives text
- the console must render characters visually on screen
- the UI may repaint frequently
That means your timing can include:
- Java output cost
- flushing behavior
- console widget overhead
- font rendering cost
- scrolling and repainting cost
Why B might be slower than #
In a normal terminal, printing B and # should be roughly the same speed. They are both just characters.
If one is dramatically slower, the most likely cause is the console renderer, not Java itself.
Possible reasons include:
- the IDE console handles some glyphs less efficiently
- font rendering for
Bis more expensive than for
Mental Model
Think of System.out.print() like sending letters to a receptionist.
- Java writes the letter quickly.
- But the receptionist still has to place each letter on a giant display board.
If one symbol is easy to draw, the board updates fast. If another symbol causes slower drawing or more complex layout work, the board becomes the bottleneck.
So your program is not really racing against character generation. It is waiting for the display system.
A good mental model is:
- CPU work = deciding what character to print
- I/O work = sending the character out
- rendering work = showing it in a console window
In this case, the rendering work dominates.
Syntax and Examples
System.out.print() writes text without adding a newline.
System.out.println() writes text and then ends the line.
Basic syntax
System.out.print("A");
System.out.println("B");
Example: printing many characters
for (int i = 0; i < 5; i++) {
System.out.print("#");
}
System.out.println();
Output:
#####
Example: building output first, then printing once
A faster and more reliable approach is to reduce the number of print calls.
import java.util.Random;
public class Main {
public static void main(String[] args) {
Random r = new ();
();
( ; i < ; i++) {
( ; j < ; j++) {
(r.nextInt() == ) {
sb.append();
} {
sb.append();
}
}
sb.append();
}
System.out.print(sb.toString());
}
}
Step by Step Execution
Consider this smaller example:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
System.out.print("B");
}
System.out.println();
}
Step-by-step
First outer loop iteration
i = 0- inner loop starts
j = 0→ printsBj = 1→ printsBj = 2→ printsB- inner loop ends
println()prints a newline
Console now shows:
BBB
Second outer loop iteration
i = 1
Real World Use Cases
This concept appears in many practical situations.
Logging
If an application logs too much text, performance can drop sharply.
Examples:
- printing every request in a web server
- logging every loop iteration in a batch job
- writing debug information for every record in a dataset
Command-line tools
CLI programs that update the terminal frequently can slow down because terminal rendering is expensive.
Examples:
- progress bars
- live dashboards
- large reports printed to screen
Data processing scripts
A script may seem slow, but the real cost is often the output, not the processing.
Examples:
- printing every parsed line from a file
- printing every API response
- dumping large JSON objects repeatedly
IDE-based experiments
Benchmarks run inside IDE consoles often measure:
- IDE UI performance
- console buffering behavior
- rendering and repaint overhead
instead of the actual speed of the code logic.
Real Codebase Usage
In real projects, developers try to avoid excessive console output and structure it carefully.
Common patterns
Buffer output
Instead of printing character by character, accumulate text first.
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println(sb.toString());
Log only what matters
Use guard clauses or log levels.
if (debugEnabled) {
System.out.println("Debug: value=" + value);
}
Use proper logging frameworks
In real applications, developers often use logging libraries instead of raw System.out.
Benefits:
- configurable log levels
- async output options
- file output instead of console spam
- better formatting
Separate benchmarking from output
If you want to benchmark logic, do not print inside the measured loop.
long start = System.nanoTime();
( ; i < ; i++) {
}
System.nanoTime();
System.out.println( + (end - start));
Common Mistakes
1. Benchmarking with console output inside the hot loop
Broken approach:
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
System.out.println(i);
}
long end = System.nanoTime();
Why it is a problem:
- measures I/O more than computation
- results vary by console and machine
Better approach:
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
int x = i * 2;
}
long end = System.nanoTime();
2. Assuming one character is always slower in Java
Broken assumption:
System.out.print("B");
Comparisons
| Scenario | What is being measured most | Reliability for benchmarking |
|---|---|---|
System.out.print() in IDE console | Console rendering + I/O + Java | Low |
System.out.print() in system terminal | Terminal I/O + rendering + Java | Medium |
| Writing to a file | File I/O + Java | Better for output testing |
Building a StringBuilder only | In-memory string construction | Good for logic/output-building |
| JMH benchmark without printing | Actual code performance | Best |
print() vs buffered construction
| Approach |
|---|
Cheat Sheet
Key idea
If printing one character seems much slower than another, the bottleneck is usually console rendering, not Java string handling.
Rules of thumb
- Do not benchmark with heavy console output inside loops.
- IDE consoles can be much slower than real terminals.
- Use
StringBuilderto reduce repeatedprint()calls. - Test in multiple environments before concluding a language feature is slow.
- Use JMH for serious Java benchmarking.
Common output methods
System.out.print("A");
System.out.println("A");
Better pattern for large text
StringBuilder sb = new StringBuilder();
sb.append('A');
sb.append('B');
System.out.print(sb.toString());
What can affect output timing?
- console buffering
- font rendering
- repaint frequency
- IDE UI performance
- terminal implementation
- OS scheduling
Quick diagnosis checklist
- Run the same code in a system terminal
- Redirect output to a file
- Replace many
print()calls with
FAQ
Why is B slower than # in Java?
Usually it is not Java itself. The slowdown is often caused by the IDE or terminal console rendering the character on screen.
Is System.out.print("B") inherently slower than System.out.print("#")?
No. In normal conditions, they should be very similar. Huge differences usually point to the output environment.
Why did the code run normally on Ideone?
Because Ideone likely handles output differently, often with more buffering and without the same live console rendering cost as NetBeans.
Why is benchmarking with console output a bad idea?
Because console output is slow and environment-dependent. You mostly measure I/O and rendering, not your algorithm.
How can I print large output faster in Java?
Build the text with StringBuilder and print once, or write to a file or buffered stream.
Does System.nanoTime() cause the issue?
No. System.nanoTime() is fine for timing, but the code being timed includes expensive console output.
How should I benchmark Java code correctly?
Avoid printing inside the measured section, warm up the JVM, run multiple iterations, and use JMH for accurate benchmarks.
Mini Project
Description
Build a small Java program that prints a random text grid in two ways: first using many print() calls, and then using a StringBuilder. This demonstrates how output strategy affects performance much more than the actual characters being printed.
Goal
Compare per-character console printing with buffered string construction and observe which approach is faster.
Requirements
- Generate a 200 x 200 grid of random
OandBcharacters. - Measure the time for printing with direct
System.out.print()calls. - Measure the time for building the same output with
StringBuilderand printing once. - Print both timing results clearly.
- Use
System.nanoTime()for timing.
Keep learning
Related questions
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Choosing a @NotNull Annotation in Java: Validation vs Static Analysis
Learn how Java @NotNull annotations differ, when to use each one, and how to choose between validation, IDE hints, and static analysis tools.
Convert a Java Stack Trace to a String
Learn how to convert a Java exception stack trace to a string using StringWriter and PrintWriter, with examples and common mistakes.