StringBuilder and StringBuffer
Strings are immutable, so every concatenation makes a new one.
A String cannot be modified. Every operation that looks like modification returns a new object:
String s = "hello";
s.toUpperCase(); // returns "HELLO", s is unchanged
s = s.toUpperCase(); // now s refers to the new string
Immutability buys safety — a string can be shared between threads and used as a map key without defensive copying. It costs when you build a string piece by piece.
The loop problem
String result = "";
for (String word : words) {
result += word; // new String every time
}
Each += allocates a new string and copies everything so far. For n words that is O(n²) copying. At a few dozen items it is invisible; at ten thousand it is a noticeable pause, and this is one of the most common accidental quadratics in ordinary code.
StringBuilder sb = new StringBuilder();
for (String word : words) {
sb.append(word);
}
String result = sb.toString();
StringBuilder keeps a mutable buffer and grows it, so appending is amortised constant. The loop is O(n).
When + is fine
For a fixed number of pieces in one expression:
String msg = "User " + name + " has " + count + " items";
The compiler turns that into a single builder. There is nothing to gain by writing it out by hand, and doing so is less readable.
The rule is simple: concatenation inside a loop needs a builder. Concatenation in one expression does not.
Useful methods
sb.append(x); // any type
sb.insert(0, "start");
sb.deleteCharAt(i);
sb.reverse();
sb.setLength(0); // reuse without reallocating
sb.length();
Give it a size if you can estimate one: new StringBuilder(1024).
StringBuffer
Same API, but every method is synchronised. It predates StringBuilder, which was added when it became clear that almost all string building is confined to one thread and paying for locking was pointless.
Use StringBuilder. Reach for StringBuffer only if a single builder is genuinely shared between threads, which is rare and usually a sign the design should change.
Joining
If you are assembling a delimited list, neither is the shortest route:
String csv = String.join(", ", words);