Memra

Immutable Strings & StringBuilder

◈ 6 cards

Why String is immutable, the string pool, == vs equals, and building text the right way.

Strings are immutable

A String in Java is immutable: once a String object exists, its characters never change. Every method that looks like it edits a string — concat, replace, toUpperCase, trim, substring, even + — actually returns a brand-new String and leaves the original untouched.

String s = "hello";
s.toUpperCase();          // returns "HELLO" — but we threw it away
System.out.println(s);    // hello   (s is unchanged)

s = s.toUpperCase();      // now we keep the new object
System.out.println(s);    // HELLO

Immutability is why strings are safe to share, safe to use as HashMap keys, and safe across threads.

The string pool and interning

String literals are interned: the JVM keeps one shared pool of literal strings, so two identical literals are the same object. But a string built at runtime (with new or by concatenating variables) is a fresh object on the heap.

String a = "java";
String b = "java";
String c = new String("java");

a == b          // true  — both refer to the one pooled literal
a == c          // false — c is a distinct new object
a.equals(c)     // true  — same characters

== compares references; .equals compares contents

This is the single most common string bug in Java. == asks "are these the same object?"; .equals asks "do these have the same characters?". For strings you almost always want .equals.

Building strings in a loop: use StringBuilder

Because every + on strings creates a new object, concatenating inside a loop is quadratic — it copies all the characters built so far on every iteration. StringBuilder holds a mutable char buffer and appends in place, then you call toString() once at the end.

Worked example. Join the numbers 0..4 with dashes.

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
    if (i > 0) sb.append('-');
    sb.append(i);
}
String result = sb.toString();   // "0-1-2-3-4"

The loop appends into one growing buffer instead of allocating a new String each pass. StringBuilder is the unsynchronized (faster, single-thread) sibling of the older StringBuffer.

a= "java"b= "java"c= new String"java"pooled literal"java"a second objecta == b is true; a == c is false.
<code>==</code> follows the arrows, not the characters. <code>a</code> and <code>b</code> reach the one interned literal, so <code>a == b</code> is true; <code>c</code> reaches a second object holding the same four characters, so <code>a == c</code> is false while <code>a.equals(c)</code> is true.
StringStringBuildercontentsimmutablemutable bufferone += in a loopallocates a new objectappends in placebuilding n charsO(n²) copyingO(n)sharingsafe: nobody can change itkeep it local to the loopchoose whenthe text is fixedassembling text piece bypiecetoString() materializes the buffer once, at the end.
The loop row is the examinable one. <code>s = s + i</code> recopies every character built so far on each pass, so joining n characters costs O(n²); one <code>StringBuilder</code> buffer appends in place and does the same job in O(n).
NORMAL ~/memra/learn/comp-308/immutable-strings-stringbuilder utf-8 LF