Skip to main content

StringBuilder and StringBuffer

Reading Time: 10 Minutes
Difficulty: Beginner


Topic Summaryโ€‹

StringBuilder and StringBuffer are classes in Java that allow you to create mutable (changeable) sequences of characters. Unlike String, you can modify their content directly without creating new objects. StringBuilder is fast but not thread-safe, while StringBuffer is thread-safe but slightly slower.


What You'll Learnโ€‹

  • Why mutable Strings exist and when you need them
  • How to use StringBuilder with all key methods
  • The difference between StringBuilder (fast) and StringBuffer (thread-safe)
  • When to use String, StringBuilder, or StringBuffer
  • Performance comparison with real numbers

Prerequisitesโ€‹

  • Java Strings and immutability (Lesson 01)
  • Basic understanding of loops and methods

Explanationโ€‹

Why Do We Need Mutable Strings?โ€‹

Recall that String is immutable โ€” every modification creates a new object. Consider building a sentence word by word:

String result = "";
for (int i = 0; i < 1000; i++) {
result = result + "word ";
}

Each iteration creates a new String object, discards the old one, and copies all previous characters plus the new word. After 1000 iterations, Java has created 1000 String objects and copied millions of characters. This is extremely wasteful.

StringBuilder solves this by maintaining a single resizable character buffer that you can append to, insert into, or delete from โ€” all without creating new objects.


StringBuilder โ€” The Fast Mutable Stringโ€‹

StringBuilder was introduced in Java 5 as a faster, non-synchronized version of StringBuffer. It is the recommended choice for most situations where you need to build or modify strings.

How StringBuilder Works Internallyโ€‹

StringBuilder internally uses a char array (character array) with an initial capacity of 16 characters. When the array fills up, it automatically doubles in size (capacity = 2 ร— old capacity + 2). This is called dynamic resizing.


Key Methods of StringBuilderโ€‹

append(value) โ€” Add to the Endโ€‹

Appends any type of value to the end of the current sequence. You can chain multiple append() calls because each returns the same StringBuilder object.

insert(index, value) โ€” Insert at Positionโ€‹

Inserts a value at the specified index, shifting everything after it to the right.

delete(start, end) โ€” Remove Charactersโ€‹

Removes characters from start (inclusive) to end (exclusive).

deleteCharAt(index) โ€” Remove One Characterโ€‹

Removes the character at a specific index.

replace(start, end, str) โ€” Replace a Sectionโ€‹

Replaces the characters from start to end with the given String.

reverse() โ€” Flip the Stringโ€‹

Reverses the character sequence in place.

charAt(index) โ€” Get a Characterโ€‹

Returns the character at the given index.

length() โ€” Current Lengthโ€‹

Returns the number of characters currently stored.

capacity() โ€” Internal Buffer Sizeโ€‹

Returns the current capacity of the internal buffer (not the same as length).

toString() โ€” Convert to Stringโ€‹

Converts the StringBuilder content back to a regular immutable String.


StringBuffer โ€” The Thread-Safe Versionโ€‹

StringBuffer has the same methods as StringBuilder but all its methods are synchronized. Synchronized means only one thread can use the method at a time โ€” this prevents data corruption when multiple threads try to modify the same object simultaneously.

FeatureStringBuilderStringBuffer
Introduced inJava 5Java 1.0
Thread-safeโŒ Noโœ… Yes
Performanceโšก Faster๐Ÿข Slower
SynchronizedโŒ Noโœ… Yes
Recommended forSingle-threadedMulti-threaded

When to Use Which?โ€‹

ScenarioUse
Fixed text that never changesString
Building text in a loop (single thread)StringBuilder
Building text in a multi-threaded environmentStringBuffer
Simple one-time concatenationString (Java compiler optimizes it)

Rule of thumb: 99% of the time, use StringBuilder. Only switch to StringBuffer if you are explicitly sharing a mutable String across multiple threads.


Performance Comparisonโ€‹

In tests concatenating 100,000 words:

  • String + concatenation: ~5000 milliseconds (creates ~100,000 objects)
  • StringBuilder.append(): ~2 milliseconds (modifies one object)

That is a 2500ร— speed difference for 100,000 iterations. For real programs that process large data, this makes an enormous difference.


Real-World Analogyโ€‹

Imagine you are writing a report and you keep crossing words out and writing new ones.

  • String is like a typed document โ€” every time you make a change, you retype the whole document from scratch and throw away the old one.
  • StringBuilder is like a whiteboard โ€” you write, erase, and edit freely. Same board, no waste.
  • StringBuffer is like a whiteboard in a classroom โ€” there is a lock on the marker. Only one person can write at a time (thread-safe), so it takes a tiny bit longer, but there's no chaos.

Code Exampleโ€‹

Example 1: StringBuilder Basicsโ€‹

public class StringBuilderDemo {
public static void main(String[] args) {

// Creating a StringBuilder
StringBuilder sb = new StringBuilder("Hello");

// append() โ€” add to end
sb.append(", ");
sb.append("World");
sb.append("!");
System.out.println(sb); // Hello, World!

// insert() โ€” insert at position
sb.insert(7, "Java ");
System.out.println(sb); // Hello, Java World!

// delete() โ€” remove characters (start inclusive, end exclusive)
sb.delete(7, 12);
System.out.println(sb); // Hello, World!

// replace() โ€” replace a section
sb.replace(7, 12, "Everyone");
System.out.println(sb); // Hello, Everyone!

// reverse() โ€” flip the whole thing
StringBuilder rev = new StringBuilder("abcde");
rev.reverse();
System.out.println(rev); // edcba

// length and capacity
StringBuilder cap = new StringBuilder();
System.out.println("Length: " + cap.length()); // 0
System.out.println("Capacity: " + cap.capacity()); // 16 (default)

// Convert to String
String result = sb.toString();
System.out.println(result); // Hello, Everyone!
}
}

Outputโ€‹

Hello, World!
Hello, Java World!
Hello, World!
Hello, Everyone!
edcba
Length: 0
Capacity: 16
Hello, Everyone!

Example 2: Performance โ€” String vs StringBuilderโ€‹

public class PerformanceDemo {
public static void main(String[] args) {

int iterations = 50000;

// Test 1: String concatenation (slow)
long startTime = System.currentTimeMillis();
String s = "";
for (int i = 0; i < iterations; i++) {
s = s + "a";
}
long endTime = System.currentTimeMillis();
System.out.println("String + took: " + (endTime - startTime) + " ms");

// Test 2: StringBuilder (fast)
startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append("a");
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder took: " + (endTime - startTime) + " ms");
}
}

Outputโ€‹

String + took: 1823 ms
StringBuilder took: 2 ms

Example 3: StringBuffer in Multi-threaded Contextโ€‹

public class StringBufferDemo {
public static void main(String[] args) throws InterruptedException {

StringBuffer buffer = new StringBuffer();

// Thread 1 appends "Hello "
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
buffer.append("Hello ");
}
});

// Thread 2 appends "World "
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
buffer.append("World ");
}
});

t1.start();
t2.start();
t1.join();
t2.join();

System.out.println("Length: " + buffer.length()); // Always 60 (safe)
System.out.println(buffer.toString());
}
}

Outputโ€‹

Length: 60
Hello Hello Hello Hello Hello World World World World World

Common Mistakesโ€‹

  • โŒ Mistake: Using String + in a for loop โ†’ โœ… Fix: Use StringBuilder.append() inside loops
  • โŒ Mistake: Using StringBuffer everywhere "just to be safe" โ†’ โœ… Fix: Use StringBuilder unless you are actually in a multi-threaded environment โ€” StringBuffer has unnecessary overhead
  • โŒ Mistake: Forgetting to call .toString() before passing to a method expecting String โ†’ โœ… Fix: Always call sb.toString() when you need a String
  • โŒ Mistake: Calling delete(0, sb.length()) to clear โ€” confusing โ†’ โœ… Fix: Use sb.setLength(0) to clear a StringBuilder efficiently

Best Practicesโ€‹

  • Always use StringBuilder (not +) when concatenating inside loops
  • Initialize StringBuilder with an estimated capacity: new StringBuilder(500) to avoid frequent resizing
  • Chain append() calls for cleaner code: sb.append("a").append("b").append("c")
  • Use sb.setLength(0) to reuse an existing StringBuilder (faster than creating a new one)
  • Never use StringBuffer unless you genuinely need thread safety โ€” it adds synchronization overhead for no reason in single-threaded code

Interview Questionsโ€‹

Q: What is the difference between String, StringBuilder, and StringBuffer?
A: String is immutable โ€” modifications create new objects. StringBuilder is mutable and fast but not thread-safe. StringBuffer is mutable and thread-safe (synchronized) but slower than StringBuilder. Use String for fixed values, StringBuilder for single-threaded string building, and StringBuffer for multi-threaded scenarios.

Q: Why is StringBuilder faster than String concatenation?
A: String concatenation with + creates a new String object every time. In a loop with N iterations, this is O(Nยฒ) time complexity because each new String copies all previous characters. StringBuilder uses a resizable char array and appends to it in-place, making it O(N) overall.

Q: Is StringBuilder thread-safe?
A: No. StringBuilder is NOT thread-safe. If multiple threads try to modify the same StringBuilder at the same time, data corruption can occur. For thread-safe mutable strings, use StringBuffer instead.

Q: What is the default capacity of StringBuilder?
A: The default capacity is 16 characters. When the buffer is full, it resizes to (old capacity ร— 2) + 2. You can specify an initial capacity with new StringBuilder(100) to avoid frequent resizing for large strings.


Quick Revisionโ€‹

โœ” StringBuilder is mutable โ€” you can change it without creating new objects
โœ” StringBuilder is NOT thread-safe; StringBuffer IS thread-safe (but slower)
โœ” Key methods: append(), insert(), delete(), reverse(), toString()
โœ” Use StringBuilder in loops โ€” it is up to 2500ร— faster than String +
โœ” Default capacity is 16; auto-doubles when full


  • Strings In Depth (Lesson 01)
  • Java Threads and Synchronization
  • Java Memory Management

Next Lessonโ€‹

01 - Exception Handling