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));
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.
- A String is not a primitive
- 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;
/* ... */
}
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:
- Stack
- Heap
- Data
- 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"
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)