Why StringBuilder
Why StringBuilder?
StringBuilder is a mutable alternative to String for efficient string manipulation.
Problem with String Concatenation
// BAD: O(n^2) time complexity
String result = "";
for (int i = 0; i < 10000; i++) {
result += i; // Creates new String each iteration!
}
// Each iteration creates:
// 1. New String for i.toString()
// 2. New String for concatenation
// Total: 10000 * 2 = 20000 String objects!
Solution with StringBuilder
// GOOD: O(n) time complexity
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // Modifies existing object
}
String result = sb.toString();
// Only 1 StringBuilder object created!
When to Use StringBuilder
// Use StringBuilder when:
// 1. Multiple concatenations in loop
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s).append(",");
}
// 2. Building strings incrementally
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("World");
// 3. String reversal
String reversed = new StringBuilder(str).reverse().toString();
// 4. Insert/delete operations
StringBuilder sb = new StringBuilder("Hello World");
sb.insert(5, " beautiful"); // "Hello beautiful World"
sb.delete(6, 16); // "Hello World"
// Use String when:
// 1. Simple concatenation (compiler optimizes)
String s = "Hello" + " " + "World";
// 2. Few concatenations
String s = firstName + " " + lastName;
// 3. String is used as map key or in comparisons
Map<String, Integer> map = new HashMap<>();
map.put("hello", 1); // Use String, not StringBuilder
Performance Comparison
// Benchmark: 10000 concatenations
// String: ~500ms
// StringBuilder: ~1ms
// The more concatenations, the bigger the difference
Methods
StringBuilder Methods
Basic Operations
StringBuilder sb = new StringBuilder("Hello");
// Append - adds to end
sb.append(" World"); // "Hello World"
sb.append(123); // "Hello World123"
sb.append(true); // "Hello World123true"
// Insert - adds at position
sb.insert(5, " cruel"); // "Hello cruel World123true"
// Delete - removes characters
sb.delete(5, 11); // "Hello World123true" (removes " cruel")
sb.deleteCharAt(0); // "ello World123true"
// Replace - replaces range
sb.replace(0, 5, "Hi"); // "Hi World123true"
// Reverse
sb.reverse(); // "eurt321dlroW iH"
Access Methods
StringBuilder sb = new StringBuilder("Hello");
// Length
int len = sb.length(); // 5
// Capacity
int cap = sb.capacity(); // 21 (16 + 5)
// Char at
char c = sb.charAt(0); // 'H'
// Index of
int idx = sb.indexOf("ll"); // 2
int idx2 = sb.indexOf("l", 3); // 3 (start searching from index 3)
// Substring
String sub = sb.substring(1, 3); // "el"
String sub2 = sb.substring(2); // "llo"
Modification Methods
StringBuilder sb = new StringBuilder("Hello");
// Set char at
sb.setCharAt(0, 'J'); // "Jello"
// Set length
sb.setLength(3); // "Jel" (truncates)
sb.setLength(10); // "Jel\\0\\0\\0\\0\\0\\0\\0" (pads with null chars)
// Ensure capacity
sb.ensureCapacity(100); // Ensure at least 100 capacity
// Trim to size
sb.trimToSize(); // Reduce capacity to current length
Method Chaining
// StringBuilder methods return this, enabling chaining
StringBuilder sb = new StringBuilder();
sb.append("Hello")
.append(" ")
.append("World")
.insert(5, ",")
.deleteCharAt(6);
// Result: "Hello World"
Converting to String
StringBuilder sb = new StringBuilder("Hello World");
String str = sb.toString(); // "Hello World"
// Important: toString() creates a new String
// After toString(), modifying sb doesn't affect str
Complete Example
public class StringBuilderMethodsDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
// Build a formatted string
sb.append("Name: ").append("Alice").append("\n");
sb.append("Age: ").append(30).append("\n");
sb.append("Active: ").append(true);
String result = sb.toString();
System.out.println(result);
// Output:
// Name: Alice
// Age: 30
// Active: true
}
}
Capacity
Capacity vs Length
StringBuilder has two important properties: length and capacity.
Understanding Capacity
// Default capacity: 16
StringBuilder sb = new StringBuilder();
int len = sb.length(); // 0
int cap = sb.capacity(); // 16
// With initial string
StringBuilder sb2 = new StringBuilder("Hello");
int len2 = sb2.length(); // 5
int cap2 = sb2.capacity(); // 21 (16 + 5)
// With initial capacity
StringBuilder sb3 = new StringBuilder(100);
int len3 = sb3.length(); // 0
int cap3 = sb3.capacity(); // 100
How Capacity Grows
StringBuilder sb = new StringBuilder();
// When capacity is exceeded, it grows:
// New capacity = (old capacity + 1) * 2
// Start: capacity = 16
// After 17 appends: capacity = 34
// After 35 appends: capacity = 70
// And so on...
// This doubling strategy makes appending O(1) amortized
Pre-allocating Capacity
// If you know approximate size, pre-allocate
StringBuilder sb = new StringBuilder(10000);
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
// No reallocation needed!
// Without pre-allocation:
// Would need ~14 reallocations (16 -> 34 -> 70 -> ...)
Capacity Operations
StringBuilder sb = new StringBuilder("Hello");
// Current capacity
int cap = sb.capacity(); // 21
// Ensure minimum capacity
sb.ensureCapacity(100); // Now capacity >= 100
// Trim to fit
sb.trimToSize(); // Capacity = length
Performance Impact
// Bad: Frequent reallocation
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("a");
// Multiple reallocations occur
}
// Good: Pre-allocate
StringBuilder sb = new StringBuilder(100000);
for (int i = 0; i < 100000; i++) {
sb.append("a");
// No reallocation
}
When to Use Capacity
// Use default capacity (16) when:
// - String size is unknown or small
// - Building string incrementally with few appends
// Pre-allocate when:
// - Known maximum size
// - Building large strings (JSON, SQL, etc.)
// - Performance-critical code
vs StringBuffer
StringBuilder vs StringBuffer
Both are mutable string builders, but with key differences.
Thread Safety
// StringBuilder: NOT thread-safe
// - No synchronization
// - Faster single-threaded performance
// StringBuffer: Thread-safe
// - All methods are synchronized
// - Slower due to synchronization overhead
// Example:
StringBuilder sb = new StringBuilder();
StringBuffer buf = new StringBuffer();
// Both have same methods
sb.append("Hello");
buf.append("Hello");
When to Use Which
// Use StringBuilder when:
// 1. Single-threaded code
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s); // No synchronization needed
}
// 2. Local variable (not shared)
public String process(String input) {
StringBuilder sb = new StringBuilder();
// sb is local, not shared
sb.append(input).reverse();
return sb.toString();
}
// Use StringBuffer when:
// 1. Multi-threaded code
public class SharedBuilder {
private StringBuffer buffer = new StringBuffer();
public void append(String s) {
buffer.append(s); // Thread-safe
}
}
// 2. When thread safety is required
// (rare in modern Java)
Performance Comparison
// StringBuilder is faster because:
// - No synchronization overhead
// - No method locking
// Benchmark (100000 concatenations):
// StringBuilder: ~10ms
// StringBuffer: ~15ms
// The difference is small for simple operations
// but can be significant in tight loops
Historical Context
// Java 1.0: Only StringBuffer existed
// Java 1.5: StringBuilder added (not thread-safe)
// Reason: Most string building is single-threaded
// StringBuilder avoids unnecessary synchronization
// Modern Java:
// - StringBuilder is preferred
// - StringBuffer is rarely used
// - Consider StringJoiner or Collectors for stream operations
Summary
| Feature | StringBuilder | StringBuffer |
|---|---|---|
| Thread-safe | No | Yes |
| Performance | Faster | Slower |
| Introduced | Java 1.5 | Java 1.0 |
| Use case | Single-thread | Multi-thread |
| Preferred | Yes | No (rarely) |
Practice Problems
Given an array of integers nums, sort the array in ascending order.
Example:
Input: nums = [5, 2, 3, 1]
Output: [1, 2, 3, 5]
Sort in ascending order
Find the kth largest element in an unsorted array.
Example:
Input: nums = [3, 2, 1, 5, 6, 4], k = 2
Output: 5
The 2nd largest element is 5
Compress string using counts of repeated characters (e.g., aabcccccaaa -> a2b1c5a3).
Example:
Input: s = "aabcccccaaa"
Output: "a2b1c5a3"
Compress consecutive characters
Quiz
1. What is the default capacity of a StringBuilder?
2. When should you use StringBuilder instead of String?
3. What is the difference between StringBuilder and StringBuffer?
Flashcards
Question
What is StringBuilder?
Click to reveal answer
Answer
A mutable sequence of characters. Unlike String, it can be modified without creating new objects.
Question
How do you convert StringBuilder to String?
Click to reveal answer
Answer
Use toString() method: StringBuilder sb = ...; String str = sb.toString();
Question
What is the time complexity of StringBuilder.append()?
Click to reveal answer
Answer
O(1) amortized. Amortized because occasional resizing is O(n) but happens rarely.
Revision Notes
Key Takeaways
- 1. StringBuilder is mutable, String is immutable
- 2. Use StringBuilder for multiple concatenations in loops
- 3. Default capacity is 16, grows by doubling
- 4. StringBuilder is not thread-safe, StringBuffer is
Interview Tips
- • Know when to use StringBuilder vs String
- • Understand capacity and how it grows
- • Remember StringBuilder is not thread-safe
- • Practice string manipulation problems
Cheat Sheet
Cheat Sheet
- Create:
new StringBuilder()ornew StringBuilder("initial") - Append:
sb.append(str) - Insert:
sb.insert(index, str) - Delete:
sb.delete(start, end) - Reverse:
sb.reverse() - Length:
sb.length() - Capacity:
sb.capacity() - To String:
sb.toString()