The hierarchy
The classes java.lang.StringBuffer
and java.lang.StringBuilder
have exactly the same interface extend java.lang.AbstractStringBuilder
, which has two main attributes:
char value[];
int count;
There are stored the characters and the actual size of the same.
About the Buffer
String
is a type that stores a character set. It is immutable, that is, it cannot have modified content, as well as its size.
The problem with this is that to create new Strings, for example, by concatenating two or more Strings, a new String as their total size must be created in memory. If several of these operations are executed in sequence, the JVM will need to allocate new memory blocks and run Garbage Collector to de-locate what is not used at all times. This is very "costly" in terms of performance.
The StringBuffer
arises to solve the problem. A StringBuffer
is nothing more than a String with a Buffer, that is, a space reserved for new characters that can be modified and makes it unnecessary, to some extent, to allocate more memory at all times.
The buffer is simply a larger character array than the actual content. For example, the buffer can have 1000 positions and the real string only 500. This is controlled by the attribute count
. In this example, we could add even more 500 characters without degrading program performance.
The initial size
In many situations it is important to set the initial buffer size to an average buffer size.
In doing new StringBuffer()
or new StringBuilder()
, that is, without setting an initial size, we are subusing the class. The initial buffer capacity is only 16 characters.
This means that if we add more than 16 characters, a new buffer will have to be allocated.
Increasing the size
When overfilling buffer capacity the next one will be created at twice the size. See the calculation of the new buffer capacity in the method expandCapacity
class AbstractStringBuilder
:
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
StringBuffer
or StringBuilder
?
This is a common question. Look at the Javadoc (links are at the beginning of the answer) and note that both have exactly the same interface, that is, the same methods and method signatures.
What is the difference? Let’s analyze a basic method in the two versions, the append(String)
.
The version on StringBuilder
is:
public StringBuilder append(String str) {
super.append(str);
return this;
}
And the version on StringBuffer
is:
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
Notice the difference? It’s the synchronized
!
We say that the class StringBuffer
is synchronized, while the class StringBuilder
is not synchronized.
Synchronized vs. Not Synchronized
What are the advantages and disadvantages of being synchronized or not?
When a class is synchronized, it is more suitable for working in multi-threaded environments, for example, on an application server that serves multiple users at the same time. Imagine several threads writing a log in memory, for example.
In this case, only one thread at a time can add content to StringBuffer
. The problem is that this also generates unwanted blocks in execution, because we are not always modifying the class.
If multiple threads just want to read some class information, then synchronization is slowing them down for no reason. Just to quote one example, the class StringBuffer
has the search method indexOf
synchronized.
When a class is not synchronized, it is not suitable to be used concurrently by more than one thread, but it gets the most leverage to be modified by a single thread and also to be used in multi-threaded reading mode.
As a general rule, if the object will be used only in the scope of a method, not being shared, it is always chosen to StringBuilder
, that was created precisely for that purpose.
If the object is shared in some way with other classes and it is possible for someone to use it in multiple threads, the StringBuffer
is more appropriate.
This reasoning applies to several other classes of the JVM. See for example the classes Hashtable (synchronized), HashMap
(non-synchronised) and ConcurrentHashMap
(synchronized only for change, but not for reading).
Therefore, it is clear the importance of knowing the language Apis well before leaving always using the same solutions indiscriminately.
For more details, see my another answer here in the OS.
Of course, in my example, the operation is not "much faster" because I made a trivial concatenation of two strings. Now try this in a loop big enough and you will feel the difference.
– André