Right arrowGo Back

Categories

String Concatenation in Java — Level 2

A developer-focused deep dive into how Java builds strings at runtime. Learn what objects are created when using the + operator, how StringBuilder avoids extra allocations, and when each approach is best.

Text

Mr. Oz

Date

Read

10 mins

Level 2

Illustration representing Java string concatenation internals
A professional headshot of a software developer in their early thirties with a friendly smile

Mr. Oz

Date

Read

10 mins

Share

Instagram logoTwitter logoYouTube logo

Java strings are immutable. Concatenation with +is compiled to use a builder under the hood, but the lifecycle of that builder depends on where the expression sits. In tight loops, using +can create many short‑lived objects; a single StringBuilderreused across iterations avoids those allocations.

  1. What gets created with +

    For a single expression like "Hello " + name + "!", the compiler emits something like new StringBuilder().append(...).toString(). That means 1 StringBuilder instance and 1 resultingString object at runtime.

    // Roughly equivalent desugaring
    String result = new StringBuilder()
      .append("Hello ")
      .append(name)
      .append("!")
      .toString();

    Inside a loop, however, s += part; becomesnew StringBuilder(s).append(part).toString() on every iteration: that is 1 new StringBuilder and 1 newString per iteration, plus the previous sbecomes eligible for GC.

    String s = "";
    for (String part : parts) {
      s += part; // each iteration: new StringBuilder + new String
    }
  2. What gets created with StringBuilder

    With an explicit StringBuilder, you typically allocate 1 builder and 1 finalString (from toString()). Intermediate steps are in the same mutable buffer. Capacity growth may copy the internal char array occasionally, but you avoid per‑iteration builder allocation.

    StringBuilder sb = new StringBuilder();
    for (String part : parts) {
      sb.append(part);
    }
    String s = sb.toString(); // one final String object
  3. Compile‑time constants are folded

    Concatenations of literals (and some final constants) are resolved at compile time with zero runtime allocation for the concatenation itself.

    String a = "Hello" + ", " + "world"; // constant folded by compiler
  4. Java 9+ note: invokedynamic concat

    Modern JDKs may use StringConcatFactory via invokedynamicfor +. The optimization strategy can change, but the guidance remains: prefer a single builder in loops or hot paths; + is fine for simple, non‑loop code.

  5. Practical guidelines

    • Use + for a handful of concatenations outside loops; it is clear and compiled efficiently.
    • In loops or builders for large strings, use StringBuilder (or StringBuffer if thread‑safe).
    • Pre‑size the builder when possible: new StringBuilder(estimatedSize).
    • Prefer String.join or streams for joining many values with delimiters.

Tips and pitfalls

  • Beware of s += x inside nested loops; allocations scale with iterations.
  • If concatenating non‑String primitives, append(int), append(double), etc. avoid boxing.
  • Benchmark with JMH for hot paths; microbenchmarks without warm‑up can mislead.

Latest Posts

A right black arrow