StringBuilder and readable formatting
Objects and text11 min readLesson 4 of 5
4.1 Why a builder
Joining two strings with + is fine for a label or a short message. Inside a loop, each + creates another string because String is immutable. A StringBuilder keeps a growable buffer and appends to it, which avoids a new string for every pass.
new StringBuilder() starts empty. append(value) adds at the end and returns the same builder, so calls can chain. length() reports the number of characters currently stored, and toString() produces a normal String for printing or for a method that expects one.
A builder is a temporary workspace. Create it near the loop, append the pieces, then convert once at the end.
public class Main {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= 3; i++) {
if (i > 1) {
builder.append(", ");
}
builder.append(i);
}
System.out.println(builder.toString());
System.out.println(builder.length());
}
}Output
1, 2, 3 7
4.1.1 Editing the buffer
insert(index, text) places text at a position and shifts the rest to the right. deleteCharAt(index) removes one character, and delete(start, end) removes a range. replace(start, end, text) swaps a range in one call. reverse() reverses the buffer in place and returns it.
Indexes follow the same rules as strings: 0 is the first character, and the end index is excluded. An index outside the current length throws StringIndexOutOfBoundsException.
These methods change the builder itself. A call such as builder.reverse(); is enough, because the returned reference points to the same object. Keep a builder local unless several methods genuinely need to share the buffer.
public class Main {
public static void main(String[] args) {
StringBuilder text = new StringBuilder("Java");
text.append(" 17");
text.insert(0, "Language: ");
text.reverse();
System.out.println(text);
text.reverse();
text.deleteCharAt(text.length() - 1);
System.out.println(text);
}
}Output
71 avaJ :egaugnaL Language: Java 1
4.1.2 printf and format
System.out.printf writes formatted text. The first argument is a format string, and the remaining arguments fill its placeholders. %s accepts text, %d accepts an integer, %f accepts a decimal number, and %.2f rounds the display to two decimal places. %n writes a line separator.
%05d pads an integer with zeros to a width of five. %8s pads text with spaces to a width of eight, aligned right by default. A minus sign, as in %-8s, aligns left. The width is a minimum, so a longer value is printed in full.
String.format uses the same placeholders and returns a string, which leaves printing to the caller. A format string can also call formatted on itself, as in "%s x %d".formatted(item, count). Use either form when the text is stored, compared, or passed to another method.
public class Main {
public static void main(String[] args) {
String name = "Asha";
int score = 92;
double average = 87.456;
System.out.printf("Name: %s%n", name);
System.out.printf("Score: %05d%n", score);
System.out.printf("Average: %.2f%n", average);
System.out.println(String.format("|%8s|%6d|", name, score));
}
}Output
Name: Asha Score: 00092 Average: 87.46 | Asha| 92|
4.1.3 Text blocks
A text block starts with three double quotes and a line break, and ends with three double quotes. It lets a multi-line string sit in the source with its line structure visible. Java removes the shared indentation based on the closing delimiter, so the text can be indented with the surrounding code.
Text blocks are useful for a short report or a JSON sample. An embedded double quote needs no escape unless three quotes appear in a row. A backslash still starts an escape sequence inside the block.
A text block keeps the line break before the closing delimiter. That detail affects lines() and any length check, so print the value while the block is new.
public class Main {
public static void main(String[] args) {
String report = """
Item: Notebook
Qty: 3
Total: 240
""";
System.out.print(report);
System.out.println("Lines: " + report.lines().count());
}
}Output
Item: Notebook Qty: 3 Total: 240 Lines: 3
4.1.4 Choosing a tool
Concatenation reads well for one or two values. StringBuilder suits repeated appends, especially in a loop. printf or String.format suits aligned output with widths and decimal places. A text block suits a fixed multi-line value.
Mixing them is normal. A report might build rows with String.format, append each row to a builder, and return builder.toString(). The choice belongs to the part of the job that dominates: repeated joining, fixed layout, or a literal block of text.
Keep format strings in one place when the same report is printed more than once. A named constant makes a change of column width a single edit.
public class Main {
public static void main(String[] args) {
String item = "Pen";
int quantity = 12;
String line = "%s x %d".formatted(item, quantity);
System.out.println(line);
StringBuilder builder = new StringBuilder(line);
builder.append(" = ready");
System.out.println(builder);
}
}Output
Pen x 12 Pen x 12 = ready
4.1.5 Practice
- Build a comma-separated list of squares from 1 to 5 with a builder.
- Insert a heading at the start of that builder and print the final text.
- Print a table with
printf: a name left aligned in 8 columns and an integer right aligned in 4. - Format a price with two decimals and an
"Rs "prefix. - Create a text block with three lines and print its line count.
For step 5, check whether the count matches the number of visible lines or includes an extra line from the closing delimiter.
