DEV Community

EronAlves1996
EronAlves1996

Posted on

String vs StringBuffer vs StringBuilder

Hi!

In a recent investigation and development of some application, I was in the middle of a doubt: it's better to use a StringBuilder in the place of a String Concatenation to print some value? Just like this:

System.out.println(new StringBuilder()
  .append("Some value ")
  .append(anIntegerVariable));
Enter fullscreen mode Exit fullscreen mode

So, in my searches I learned a lot and wanna to share my new knowledge about strings with you.

What in fact is a String?

Perharps it appears to be primitives, or just an unnecessary class on Java API, the treatment Java does to it is correct.

  1. A String is not a primitive
  2. A String is a reference value, but is always constant

Let's clarify these two facts.
Primitive values are self-sufficient, independent and don't depend on another values to be defined. On the other side, complex values are composed by a set of some primitive values.
If you already have some contact with C lang, you noted that strings don't exists in it. In fact, what we have is a char array, and in the memory, is exactly what a string is.

So, we have some fixed range in the memory that will store our char array. Arrays are immutable by default, so it means that a String is immutable.

By the way, Java will wrap this char array in a String class and treat it as a reference value.
In more recent versions, Java gonna treat a string as a byte Array, and no more like a char Array

To clarify this, let's see the very beginning code of String class to see what in fact it is:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

   // Stores the "string" inside the String object
    @Stable
    private final byte[] value;

    private final byte coder;

    private boolean hashIsZero; 

    @java.io.Serial
    private static final long serialVersionUID = -6849794470754667710L;


    static final boolean COMPACT_STRINGS;
/* ... */
}
Enter fullscreen mode Exit fullscreen mode

So, the first fact was clarified.

The second fact is that a String is always a constant reference, no matters what happens in the program. It nevers get cleaned by the Garbage Collector, but that doesn't means that it represents a memory leak.

We should remember that, when our program runs, it gonna be executed inside the JVM, and the Operational System takes it as a process. Process usually have four sections:

  1. Stack
  2. Heap
  3. Data
  4. Text

When we hardcode some String into our code, every time it runs, the String will be ever present on the Text session of process, which stores all the program code.
To help in the concept, Rust gives a very explicit treatment to it. The language treat every string as having 'static lifetime. It means that in the entire process life cycle, the string lives in it, even if it don't have no references to it.
So the second fact is clarified.

Compiler Optimizations

The compilation process have a clear advantage over the direct interpretation. It can make some optimizations on the code ahead of time, reducing the overhead of Just-In-Time optimization.

In the case of strings, every time we declare a string literal, we are creating a String object. Let's see some examples:


// one string object
"a string"

// two string objects
"two" + "strings"

// three string objects
"There are " + "three" + "strings"
Enter fullscreen mode Exit fullscreen mode

So, a string concatenation don't help on this. In fact, it's worse: the JVM create the objects and after it, create more objects to represent the intermediate/final result of concatenation.

To prevent this behavior, the Java Compiler make some static analysis to convert this String concatenation to StringBuffer or StringBuilder.

StringBuffer and StringBuilder

These two classes make possible a single String mutable object. So, we can append or insert some string or value inside these classes to craft a String and reduce overhead.

What's the difference between them?

The StringBuffer is suited for secure multi-threaded access.
The StringBuilder is suited for performant single-threaded access.

So, what should I use?

  • For manipulation of a String in a single context (print methods, concatenation on same scope), use just String/String concatenation;
  • For manipulation of a String in multiple context on a single thread (loops, concat results or logs of multiples methods on a single string, etc), use StringBuilder;
  • For the former case, on multiple threads, use StringBuffer.

Top comments (0)