Strings: StringBuilder & StringBuffer
Mutable string handling
1. Introduction to Mutable Strings
While String objects are immutable, Java provides StringBuilder and StringBuffer for efficient string manipulation when you need to modify text content frequently.
Why Mutable String Classes?
- Performance: Avoid creating multiple temporary String objects
- Memory Efficiency: Reduce garbage collection pressure
- Flexibility: Enable efficient insertion, deletion, and modification
- Scalability: Handle large text processing operations
String
Immutable, Thread-safe, Simple usage
StringBuilder
Mutable, Not thread-safe, High performance
StringBuffer
Mutable, Thread-safe, Synchronized
Problem: Inefficient String Concatenation
// INEFFICIENT: Creates many temporary String objects
String result = "";
for (int i = 0; i < 1000; i++) {
result += "Item " + i + "; "; // 3000+ objects created!
}
// EFFICIENT: StringBuilder approach
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("Item ").append(i).append("; "); // Modifies same object
}
String result = sb.toString();
2. StringBuilder Fundamentals
StringBuilder provides a mutable sequence of characters with methods for efficient text manipulation.
Creating StringBuilder Objects
StringBuilder Creation
// Default constructor (16 character capacity)
StringBuilder sb1 = new StringBuilder();
// With initial capacity
StringBuilder sb2 = new StringBuilder(100);
// With initial content
StringBuilder sb3 = new StringBuilder("Initial content");
// From another sequence
StringBuilder sb4 = new StringBuilder(sb3);
// Check properties
System.out.println("Length: " + sb3.length()); // Current content length
System.out.println("Capacity: " + sb3.capacity()); // Available space
Basic StringBuilder Operations
Core StringBuilder Methods
StringBuilder sb = new StringBuilder();
// Appending content
sb.append("Hello"); // "Hello"
sb.append(' '); // "Hello "
sb.append("World"); // "Hello World"
sb.append(123); // "Hello World123"
sb.append(45.67); // "Hello World12345.67"
// Method chaining for fluent API
StringBuilder chain = new StringBuilder()
.append("Java")
.append(" is")
.append(" awesome");
// Insertion at specific position
StringBuilder text = new StringBuilder("Hello World");
text.insert(5, ","); // "Hello, World"
text.insert(0, "Hi! "); // "Hi! Hello, World"
// Getting the final string
String result = text.toString(); // Convert to immutable String
StringBuilder Modification Methods
Modifying StringBuilder Content
StringBuilder sb = new StringBuilder("Hello World Programming");
// Deletion operations
sb.delete(5, 11); // "Hello Programming" (delete " World")
sb.deleteCharAt(5); // "HelloProgramming" (delete space)
// Replacement operations
StringBuilder text = new StringBuilder("Java Programming");
text.replace(0, 4, "Python"); // "Python Programming"
// Character manipulation
text.setCharAt(0, 'p'); // "python Programming"
char firstChar = text.charAt(0); // 'p'
// String reversal
StringBuilder reversed = new StringBuilder("Hello");
reversed.reverse(); // "olleH"
// Length manipulation
sb.setLength(5); // Truncate to 5 characters
sb.setLength(10); // Extend with null characters
3. StringBuffer and Thread Safety
StringBuffer provides the same functionality as StringBuilder but with synchronized methods for thread-safe operations.
StringBuffer Characteristics
- Thread-Safe: All methods are synchronized
- Legacy Class: Predates StringBuilder (Java 1.0)
- Performance Cost: Synchronization overhead in single-threaded scenarios
- API Identical: Same methods as StringBuilder
StringBuffer Usage
// StringBuffer creation (same as StringBuilder)
StringBuffer buffer = new StringBuffer();
StringBuffer buffer2 = new StringBuffer(50);
StringBuffer buffer3 = new StringBuffer("Initial");
// All methods are synchronized (thread-safe)
buffer.append("Thread-safe");
buffer.append(" operations");
buffer.insert(0, "Safe: ");
// Thread-safe example
public class ThreadSafeLogger {
private StringBuffer logBuffer = new StringBuffer();
public void log(String message) {
// Safe for concurrent access
logBuffer.append(new Date())
.append(": ")
.append(message)
.append("\n");
}
public String getLog() {
return logBuffer.toString();
}
}
4. Performance Analysis
Understanding the performance characteristics of different string building approaches helps you make informed decisions.
Performance Comparison
Benchmarking String Operations
public class StringPerformanceTest {
public static void main(String[] args) {
int iterations = 10000;
// Test 1: String concatenation (SLOW)
long start = System.nanoTime();
String str = "";
for (int i = 0; i < iterations; i++) {
str += "x"; // O(n²) complexity
}
long stringTime = System.nanoTime() - start;
// Test 2: StringBuilder (FAST)
start = System.nanoTime();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append("x"); // O(n) complexity
}
String sbResult = sb.toString();
long sbTime = System.nanoTime() - start;
// Test 3: StringBuffer (SLOWER due to synchronization)
start = System.nanoTime();
StringBuffer buff = new StringBuffer();
for (int i = 0; i < iterations; i++) {
buff.append("x"); // O(n) but synchronized
}
String buffResult = buff.toString();
long buffTime = System.nanoTime() - start;
System.out.printf("String concat: %d ms%n", stringTime / 1_000_000);
System.out.printf("StringBuilder: %d ms%n", sbTime / 1_000_000);
System.out.printf("StringBuffer: %d ms%n", buffTime / 1_000_000);
}
}
Performance Summary
String Concatenation: O(n²) complexity, many temporary objects StringBuilder: O(n) complexity, single mutable object StringBuffer: O(n) complexity with synchronization overhead
5. Advanced Usage Patterns
Advanced techniques for leveraging StringBuilder and StringBuffer in complex scenarios.
Builder Pattern Implementation
Custom Builder with StringBuilder
public class SqlQueryBuilder {
private final StringBuilder query;
public SqlQueryBuilder() {
this.query = new StringBuilder();
}
public SqlQueryBuilder select(String... columns) {
query.append("SELECT ");
query.append(String.join(", ", columns));
return this;
}
public SqlQueryBuilder from(String table) {
query.append(" FROM ").append(table);
return this;
}
public SqlQueryBuilder where(String condition) {
query.append(" WHERE ").append(condition);
return this;
}
public String build() {
return query.toString();
}
}
// Usage example
String sql = new SqlQueryBuilder()
.select("id", "name", "email")
.from("users")
.where("active = 1")
.build();
6. Best Practices & Guidelines
Follow these guidelines to use StringBuilder and StringBuffer effectively in your applications.
Best Practice Examples
// GOOD: Pre-sized StringBuilder
public static String buildCsvRow(String[] values) {
int estimatedSize = values.length * 20;
StringBuilder csv = new StringBuilder(estimatedSize);
for (int i = 0; i < values.length; i++) {
if (i > 0) csv.append(",");
csv.append(values[i]);
}
return csv.toString();
}
// GOOD: Reusable StringBuilder
public class LogFormatter {
private final StringBuilder reusableBuilder = new StringBuilder(256);
public String formatLogEntry(String level, String message) {
reusableBuilder.setLength(0); // Clear previous content
reusableBuilder.append("[")
.append(new Date())
.append("] ")
.append(level)
.append(": ")
.append(message);
return reusableBuilder.toString();
}
}
Part 2 Summary
Part 2 covered the essential mutable string classes that enable efficient string manipulation in Java applications.
Key Takeaways
- StringBuilder: Use for efficient string building in single-threaded scenarios
- StringBuffer: Use only when you need built-in thread safety
- Performance: StringBuilder is 100x+ faster than String concatenation in loops
- Capacity Management: Pre-size when possible to avoid resizing overhead
- Modern Alternatives: Consider String.join() for simple cases