When working with strings in Java, you’ll often need to build strings piece by piece or modify them frequently. While the String class is great for many purposes, it has a limitation: strings are immutable, meaning they cannot be changed once created.
For situations where you need to modify strings frequently, Java provides two powerful alternatives: StringBuilder and StringBuffer.
Let’s first understand why we need StringBuilder. Consider this code:
public class ZipCode {
void compute() {
String message = "Hello";
message = message + " ";
message = message + "World";
message = message + "!";
System.out.println(message); // "Hello World!"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();What’s happening behind the scenes is that Java creates a new String object every time you use the + operator.
So this simple code actually creates 4 different String objects in memory!
StringBuilder allows you to efficiently build and modify strings without creating new objects each time.
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
sb.append("!");
String result = sb.toString();
System.out.println(result); // "Hello World!"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();In this example, we create a StringBuilder object and use the append() method to add pieces of text.
Finally, we convert it back to a regular String with toString().
// Create StringBuilder with initial capacity
StringBuilder sb = new StringBuilder(50); // Can hold 50 characters initially
// Create StringBuilder with initial content
StringBuilder sb2 = new StringBuilder("Starting text");You can create a StringBuilder with an initial capacity to optimize memory usage if you know the approximate size of the final string. You can also initialize it with some starting text.
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder();
sb.append("Java");
sb.append(" is");
sb.append(" awesome");
sb.append('!'); // Can append single characters
sb.append(2024); // Can append numbers
System.out.println(sb.toString()); // "Java is awesome!2024"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example shows how to use append() to build a string piece by piece.
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder("Hello World");
sb.insert(6, "Beautiful "); // Insert at position 6
System.out.println(sb.toString()); // "Hello Beautiful World"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example shows how to insert "Beautiful " into "Hello World".
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder("Hello World!");
sb.delete(5, 11); // Delete from index 5 to 11
System.out.println(sb.toString()); // "Hello!"
sb.deleteCharAt(5); // Delete character at index 5
System.out.println(sb.toString()); // "Hello"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This is how to delete a range of characters and a single character.
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder("Hello World");
sb.replace(6, 11, "Java"); // Replace "World" with "Java"
System.out.println(sb.toString()); // "Hello Java"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example shows how to replace "World" with "Java".
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder("Hello");
sb.reverse();
System.out.println(sb.toString()); // "olleH"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example shows how to reverse the string "Hello" to "olleH".
This is just a sampling of the many methods available in StringBuilder. You can also get the length, set the length, and more.
Here’s a practical example showing how StringBuilder is useful for building formatted text:
public class ZipCode {
void compute() {
StringBuilder html = new StringBuilder();
html.append("<html>\n");
html.append("<head><title>My Page</title></head>\n");
html.append("<body>\n");
html.append("<h1>Welcome!</h1>\n");
html.append("<p>This page was generated with StringBuilder.</p>\n");
html.append("</body>\n");
html.append("</html>");
System.out.println(html.toString());
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example builds a simple HTML page piece by piece using StringBuilder.
StringBuilder methods return the StringBuilder object itself, allowing you to chain operations:
public class ZipCode {
void compute() {
StringBuilder sb = new StringBuilder();
String result = sb.append("Hello")
.append(" ")
.append("World")
.append("!")
.reverse()
.toString();
System.out.println(result); // "!dlroW olleH"
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example shows how to chain multiple append() calls and other methods together for concise code.
There are two similar classes for mutable strings:
| Feature | StringBuilder | StringBuffer |
|---|---|---|
Thread Safety |
Not thread-safe |
Thread-safe (synchronized) |
Performance |
Faster |
Slower (due to synchronization) |
When to Use |
Single-threaded programs (most cases) |
Multi-threaded programs where multiple threads access the same string |
For most Java programs, StringBuilder is the preferred choice because it’s faster and most programs don’t need thread safety.
Here’s why StringBuilder is much more efficient:
public class ZipCode {
void compute() {
// Inefficient - creates many String objects
long start1 = System.currentTimeMillis();
String slow = "";
for (int i = 0; i < 10000; i++) {
slow = slow + "a"; // Creates new String object each time!
}
long end1 = System.currentTimeMillis();
System.out.println("String concatenation time: " + (end1 - start1) + " ms");
// Efficient - modifies existing StringBuilder
long start2 = System.currentTimeMillis();
StringBuilder fast = new StringBuilder();
for (int i = 0; i < 10000; i++) {
fast.append("a"); // Modifies existing object
}
String result = fast.toString();
long end2 = System.currentTimeMillis();
System.out.println("StringBuilder time: " + (end2 - start2) + " ms");
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();When you run this code, you’ll see that using StringBuilder is significantly faster than using String concatenation in a loop.
This has to do with the effects of something called "memory allocation" and "garbage collection" in Java, which are advanced topics beyond the scope of this introduction. But the key takeaway is: use StringBuilder for building strings in loops or when performance matters.
Use StringBuilder when you need to:
-
Build strings in loops - Especially when you don’t know the final size
-
Frequently modify strings - Adding, removing, or changing parts
-
Build formatted output - Like HTML, XML, or reports
-
Optimize performance - When string operations are performance-critical
Use regular String when you:
-
Have fixed text that won’t change
-
Do simple concatenation of just a few strings
-
Want immutable data for safety
public class ZipCode {
void compute() {
StringBuilder report = new StringBuilder();
// Header
report.append("=== STUDENT REPORT ===\n");
report.append("Generated on: 2024-01-15\n\n");
// Student data
String[] names = {"Alice", "Bob", "Charlie"};
int[] grades = {85, 92, 78};
for (int i = 0; i < names.length; i++) {
report.append("Student: ").append(names[i]);
report.append(", Grade: ").append(grades[i]);
if (grades[i] >= 90) {
report.append(" (Excellent!)");
} else if (grades[i] >= 80) {
report.append(" (Good)");
}
report.append("\n");
}
// Footer
report.append("\n--- End of Report ---");
System.out.println(report.toString());
}
public static void main(String[] args) { new ZipCode().compute(); }
}
// to run in jshell:
new ZipCode().compute();This example builds a student report with names and grades, demonstrating how StringBuilder can be used for structured text generation.
| Method | Description |
|---|---|
|
Add value to the end |
|
Insert value at specific position |
|
Remove characters from start to end |
|
Remove character at specific index |
|
Replace characters with new string |
|
Reverse the entire string |
|
Convert to regular String |
|
Get current length |
|
Set length (truncate or pad with nulls) |
StringBuilder is an essential tool for efficient string manipulation in Java. Master it to write faster, more memory-efficient programs!
Use it often.