00:00

Difference Between String and StringBuffer in Java

Last updated: 4 min read

String and StringBuffer both represent sequences of characters, but they behave very differently:

  • String is immutable. Every "change", such as concatenation or toUpperCase(), creates a new object. Literals are shared through the string pool.
  • StringBuffer is mutable. Methods such as append, insert and reverse modify the same object, and every method is synchronized.

1. Comparison Table

AspectStringStringBuffer
MutabilityImmutableMutable
Thread safetySafe (cannot change)Safe (synchronized methods)
Repeated modificationSlow: creates new objectsFast: modifies an internal buffer
StorageLiterals in the string pool; objects on the heapHeap only
equals()Compares contentCompares references (not overridden)
hashCode()Content-based, cachedIdentity-based
Concatenation+, concat()append()
Extra operationsRich read-only APIinsert, delete, reverse, setCharAt, capacity control
Good as a map keyYesNo

2. Immutable vs Mutable

MutabilityDemo.java
public class MutabilityDemo {
    public static void main(String[] args) {
        String s = "Java";
        String s2 = s.concat(" Tutorial");          // new object; s unchanged
        System.out.println("String       : s = " + s + ", s2 = " + s2 + ", same object? " + (s == s2));

        StringBuffer sb = new StringBuffer("Java");
        StringBuffer sb2 = sb.append(" Tutorial");  // same object modified in place
        System.out.println("StringBuffer : sb = " + sb + ", same object? " + (sb == sb2));

        // equals(): String compares content, StringBuffer compares identity
        System.out.println("\"ab\".equals(new String(\"ab\"))           : " + "ab".equals(new String("ab")));
        System.out.println("new StringBuffer(\"ab\").equals(another)   : " + new StringBuffer("ab").equals(new StringBuffer("ab")));
        System.out.println("contentEquals                           : " + "ab".contentEquals(new StringBuffer("ab")));

        // Converting between them
        String back = sb.toString();
        StringBuffer fromString = new StringBuffer(back).reverse();
        System.out.println("toString / reverse : " + back + " / " + fromString);
    }
}
Output
String       : s = Java, s2 = Java Tutorial, same object? false
StringBuffer : sb = Java Tutorial, same object? true
"ab".equals(new String("ab"))           : true
new StringBuffer("ab").equals(another)   : false
contentEquals                           : true
toString / reverse : Java Tutorial / lairotuT avaJ

Be careful with equals: two buffers with the same text are not equal. Compare their toString() values or use contentEquals.

3. Performance of Repeated Concatenation

ConcatCost.java
public class ConcatCost {
    public static void main(String[] args) {
        int n = 30_000;
        long t0 = System.nanoTime();
        String s = "";
        for (int i = 0; i < n; i++) s += "x";
        long t1 = System.nanoTime();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < n; i++) sb.append("x");
        long t2 = System.nanoTime();
        System.out.printf("String +=           : %4d ms%n", (t1 - t0) / 1_000_000);
        System.out.printf("StringBuffer.append : %4d ms%n", (t2 - t1) / 1_000_000);
        System.out.println("same text: " + s.equals(sb.toString()));
    }
}
Output (sample run; timings vary)
String +=           :   49 ms
StringBuffer.append :    1 ms
same text: true

Each += copies the whole string built so far, making loops quadratic. append adds to a growing buffer instead.

4. When to Use Which

  • String: text that does not change, constants, map keys, method parameters and return values. This is almost always the right type to expose in APIs.
  • StringBuilder (not StringBuffer): building text in loops within one thread, which is the usual case.
  • StringBuffer: only when one buffer is genuinely shared between threads, or a legacy API requires it.

See StringBuffer vs StringBuilder.

5. Summary

  • String is immutable; StringBuffer is mutable and synchronized.
  • Repeated changes are much faster with a buffer than with String concatenation.
  • String.equals compares content; StringBuffer.equals compares identity.
  • Use String for values, StringBuilder for building, StringBuffer only for shared buffers.

Related Tutorials