String vs StringBuilder vs StringBuffer in Java

Anni HuangAnni Huang
6 min read

Comprehensive Comparison Table

FeatureString ๐Ÿ“œStringBuilder โšกStringBuffer ๐Ÿ”’
Mutability ๐Ÿ”„ImmutableMutableMutable
Thread Safety ๐Ÿ›ก๏ธThread-safe (immutable)โŒ Not thread-safeโœ… Thread-safe (synchronized)
Performance ๐ŸŽ๏ธโŒ Slow for concatenationsโœ… FastModerate (slower than StringBuilder)
Memory Usage ๐Ÿ’พโŒ High for multiple operationsโœ… Lowโœ… Low
Package ๐Ÿ“ฆjava.langjava.langjava.lang
Introduced ๐Ÿ“…JDK 1.0JDK 1.5JDK 1.0
Synchronization โš™๏ธN/ANo synchronizationโœ… Synchronized methods
Best Use Case ๐ŸŽฏConstants, simple operationsSingle-threaded string buildingMulti-threaded string building

Detailed Feature Comparison

Memory and Performance

AspectStringStringBuilderStringBuffer
Creation Speed ๐Ÿš€FastFastFast
Concatenation Speed ๐Ÿ”—Very Slow (O(nยฒ))โœ… Very Fast (O(1) amortized)Fast (O(1) amortized)
Memory Overhead ๐Ÿ“ŠHigh (creates new objects)Low (reuses buffer)Low (reuses buffer)
Buffer Management ๐Ÿ—‚๏ธN/Aโœ… Automatic resizeโœ… Automatic resize
Initial Capacity ๐Ÿ“N/A16 characters16 characters
Capacity Growth ๐Ÿ“ˆN/A(old capacity ร— 2) + 2(old capacity ร— 2) + 2

Method Availability

Method CategoryStringStringBuilderStringBuffer
Append Operations โž•โŒ (creates new objects)โœ… append()โœ… append()
Insert Operations ๐Ÿ“โŒโœ… insert()โœ… insert()
Delete Operations ๐Ÿ—‘๏ธโŒโœ… delete(), deleteCharAt()โœ… delete(), deleteCharAt()
Replace Operations ๐Ÿ”„โœ… replace() (new object)โœ… replace() (modifies)โœ… replace() (modifies)
Reverse Operation โ†ฉ๏ธโŒโœ… reverse()โœ… reverse()
Capacity Management ๐Ÿ“ฆโŒโœ… capacity(), ensureCapacity()โœ… capacity(), ensureCapacity()
String Conversion ๐Ÿ”„N/A (already String)โœ… toString()โœ… toString()

Thread Safety Details

ScenarioStringStringBuilderStringBuffer
Single Thread ๐Ÿงตโœ… Safeโœ… Optimal choiceโœ… Safe but overkill
Multiple Threads Reading ๐Ÿ‘ฅ๐Ÿ“–โœ… Safeโš ๏ธ Safe if no modificationsโš ๏ธ Safe if no modifications
Multiple Threads Writing ๐Ÿ‘ฅโœ๏ธโœ… Safe (immutable)โŒ Race conditionsโœ… Safe (synchronized)
Performance in Multithreading ๐ŸGoodN/A (unsafe)Moderate (lock overhead)

Memory Footprint Comparison

OperationString Memory ImpactStringBuilder Memory ImpactStringBuffer Memory Impact
1000 Concatenations~500MB (1000 objects)~16KB (1 buffer)~16KB (1 buffer)
Empty Creation~40 bytes~56 bytes~72 bytes (sync overhead)
With 50 chars~140 bytes~156 bytes~172 bytes

Performance Benchmarks

Concatenation Performance (10,000 operations)

ClassTime (ms)Memory Objects CreatedRelative Performance
String~500ms10,0001x (baseline)
StringBuilder~1ms1500x faster
StringBuffer~3ms1167x faster

Thread Safety Performance

ScenarioStringBuilderStringBufferPerformance Difference
Single Thread1ms3msStringBuilder 3x faster
2 ThreadsUnsafe5msN/A
4 ThreadsUnsafe8msN/A
8 ThreadsUnsafe15msN/A

Code Examples Comparison

Basic Usage

// String - Immutable
String str = "Hello";
str = str + " World";        // Creates new object
str = str + "!";             // Creates another new object
// Result: 3 objects created

// StringBuilder - Mutable, Not Thread-Safe
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");         // Modifies existing buffer
sb.append("!");              // Modifies same buffer
String result = sb.toString(); // Convert to String

// StringBuffer - Mutable, Thread-Safe
StringBuffer buffer = new StringBuffer("Hello");
buffer.append(" World");     // Synchronized method
buffer.append("!");          // Synchronized method
String result2 = buffer.toString();

Multi-threading Examples

// StringBuilder - NOT thread-safe
public class UnsafeExample {
    private StringBuilder sb = new StringBuilder();

    public void appendText(String text) {
        sb.append(text); // Race condition possible!
    }
}

// StringBuffer - Thread-safe
public class SafeExample {
    private StringBuffer buffer = new StringBuffer();

    public void appendText(String text) {
        buffer.append(text); // Thread-safe
    }
}

// Manual synchronization with StringBuilder
public class ManualSyncExample {
    private StringBuilder sb = new StringBuilder();
    private final Object lock = new Object();

    public void appendText(String text) {
        synchronized(lock) {
            sb.append(text); // Now thread-safe
        }
    }
}

When to Use Each

Use String When ๐Ÿ“œ

  • Constants and literals
  • Simple concatenations (1-2 operations)
  • Immutable data requirements
  • API parameters and return values
  • Thread safety without performance concerns
// Good String usage
public static final String ERROR_MSG = "Error occurred";
String name = firstName + " " + lastName;
String filePath = directory + "/" + filename;

Use StringBuilder When โšก

  • Multiple concatenations
  • Single-threaded environments
  • Performance is critical
  • Building large strings dynamically
  • Most common choice for string building
// Good StringBuilder usage
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM users WHERE ");
for (String condition : conditions) {
    sql.append(condition).append(" AND ");
}

Use StringBuffer When ๐Ÿ”’

  • Multi-threaded string building
  • Legacy code compatibility
  • Thread safety is mandatory
  • Concurrent access to shared string builder
// Good StringBuffer usage
public class LogBuffer {
    private StringBuffer buffer = new StringBuffer();

    public synchronized void addLog(String message) {
        buffer.append(new Date()).append(": ").append(message).append("\n");
    }

    public synchronized String getLogs() {
        return buffer.toString();
    }
}

Migration Guidelines

String โ†’ StringBuilder

// Before (inefficient)
String result = "";
for (String item : items) {
    result += item + ", ";
}

// After (efficient)
StringBuilder sb = new StringBuilder();
for (String item : items) {
    sb.append(item).append(", ");
}
String result = sb.toString();

StringBuffer โ†’ StringBuilder

// Before (thread-safe but slower)
StringBuffer buffer = new StringBuffer();
buffer.append("text");

// After (faster, manual sync if needed)
StringBuilder sb = new StringBuilder();
synchronized(lock) {
    sb.append("text");
}

Best Practices Summary

PracticeStringStringBuilderStringBuffer
Default ChoiceSimple operationsComplex string buildingMulti-threaded building
Capacity SettingN/ASet initial capacitySet initial capacity
ReuseCreate newClear and reuseClear and reuse
Thread SafetyInherentManual if neededAutomatic
PerformanceAccept for simple opsOptimize for complexAccept sync overhead

Conclusion

  • String: Choose for simplicity and immutability requirements
  • StringBuilder: Choose for performance in single-threaded scenarios (most common)
  • StringBuffer: Choose only when thread safety is required

In modern Java development, StringBuilder is the go-to choice for string building operations, with StringBuffer reserved for specific multi-threading needs.

0
Subscribe to my newsletter

Read articles from Anni Huang directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anni Huang
Anni Huang

I am Anni HUANG, a software engineer with 3 years of experience in IDE development and Chatbot.