DEV Community

Cover image for StringBuilder vs StringBuffer in Java
Arshi Saxena
Arshi Saxena

Posted on

7 3 1 1 4

StringBuilder vs StringBuffer in Java

In Java, when working with mutable strings (strings that can be modified), you may need to choose between StringBuilder and StringBuffer. While both are mutable classes that allow modification of their values, they differ significantly in terms of thread safety, performance, and application. Here, we’ll compare their characteristics and provide code examples to illustrate when to use each one.


Key Differences: StringBuilder vs. StringBuffer

Feature StringBuilder StringBuffer
Mutability Mutable Mutable
Stored in Heap (does not use String Pool) Heap (does not use String Pool)
Thread Safety Not thread-safe Thread-safe
Synchronization Not synchronized Synchronized
Performance Faster due to lack of synchronization Slower due to synchronization overhead
Use Case Single-threaded scenarios Multi-threaded scenarios where thread-safety is required

Let’s explore each class in more detail.


1. StringBuilder: The Efficient Choice for Single-Threaded Environments

  • StringBuilder is a mutable class, meaning it allows modifications to its content.

  • It is thread-unsafe, so it’s ideal for single-threaded scenarios.

  • Not synchronized: StringBuilder is faster than StringBuffer due to the absence of synchronization overhead.

  • Multi-threaded limitation: Using StringBuilder in multi-threaded environments without additional safety measures can lead to race conditions and other concurrency issues.

Example: Demonstrating Thread Unsafety in StringBuilder

In this example, we use two threads to append characters to a StringBuilder instance. However, due to the lack of synchronization, we encounter race conditions:

public class StringBuilderBasics {

    public void threadUnsafe() {
        // Common resource being shared
        StringBuilder builder = new StringBuilder();

        // Thread appending "A" 1000 times
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                builder.append("A");
            }
        });

        // Thread appending "B" 1000 times
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                builder.append("B");
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Result: 1840 (unpredictable)
        System.out.println("Length: " + builder.toString().length());
    }

    public static void main(String[] args) {
        new StringBuilderBasics().threadUnsafe();
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Due to thread-unsafety, the final length of the StringBuilder output is unpredictable (e.g., 1840 instead of 2000).

  • This happens because both threads attempt to append characters simultaneously, leading to overwrites or dropped operations.

Takeaway: Use StringBuilder only in single-threaded environments or when thread-safety is handled externally.


2. StringBuffer: The Safe Option for Multi-Threaded Environments

  • StringBuffer is mutable, allowing modifications to its content.

  • It is synchronized, which makes it thread-safe.

  • Ideal for multi-threaded environments where thread safety is necessary.

  • Performance cost: Synchronization introduces overhead, so StringBuffer is slower than StringBuilder.

Example: Thread Safety in StringBuffer

Here’s the same example as above, but this time using StringBuffer:

public class StringBufferBasics {

    public void threadSafe() {
        // Common resource being shared
        StringBuffer buffer = new StringBuffer();

        // Thread appending "A" 1000 times
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                buffer.append("A");
            }
        });

        // Thread appending "B" 1000 times
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                buffer.append("B");
            }
        });

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

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Result: 2000
        System.out.println("Length: " + buffer.toString().length());
    }

    public static void main(String[] args) {
        new StringBufferBasics().threadSafe();
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • StringBuffer ensures that both threads append safely, achieving the expected length of 2000.

  • While the final string is thread-safe, the output may be interleaved (e.g., “AAABBB...” mixed together) as thread execution order is non-deterministic.

Takeaway: Use StringBuffer for multi-threaded applications where data consistency is crucial and synchronization is needed.


Choosing the Right Class

To decide between StringBuilder and StringBuffer, consider the following:

  • Use StringBuilder in single-threaded scenarios where performance is critical and thread-safety isn’t a concern.

  • Use StringBuffer in multi-threaded scenarios where you need mutable string operations and require thread safety to avoid race conditions.


Conclusion

This comparison should help you make an informed choice between StringBuilder and StringBuffer. Understanding the trade-offs in mutability, performance, and thread safety can lead to better decision-making when working with strings in Java.


Related Posts

Happy Coding!

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (2)

Collapse
 
devmercy profile image
Mercy

Finally getting to know the difference, thank you

Collapse
 
arshisaxena26 profile image
Arshi Saxena

You're Welcome :)

Great read:

Is it Time to go Back to the Monolith?

History repeats itself. Everything old is new again and I’ve been around long enough to see ideas discarded, rediscovered and return triumphantly to overtake the fad. In recent years SQL has made a tremendous comeback from the dead. We love relational databases all over again. I think the Monolith will have its space odyssey moment again. Microservices and serverless are trends pushed by the cloud vendors, designed to sell us more cloud computing resources.

Microservices make very little sense financially for most use cases. Yes, they can ramp down. But when they scale up, they pay the costs in dividends. The increased observability costs alone line the pockets of the “big cloud” vendors.

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay