How do Stringbuffer() and Stringbuilder() behave?

Asked

Viewed 1,866 times

11

Problem and Mystery:


StringBuffer y = new StringBuffer("Eduardo");

I’m wearing a StringBuffer, because I need to add some values later to it. I was debugging and noticed that there is a blank at the end. Note the image:

Espaços no StringBuffer Image 1: Additional Spaces in Stringbuffer.


The same happens when I use the StringBuilder, someone knows why these spaces are incremented?

There are some aspects between StringBuilder and StringBuffer. How to use the Builder when it does not involve Thread. I believe I have not reversed.

4 answers

14


White spaces are to improve performance when concatenating strings. That’s why it’s a "buffer".

For example: if you concatenate two Strings like this: "abc" + "def", Java will create, in addition to these two String objects, a third object containing the "abcdef" result. However, when using the buffer, Java will play chars’d, 'e' and 'f' within the buffer voids and update that 'Count' of your figure to reflect the new size of the set. This is a much faster operation.

If white spaces are not enough, Java will automatically increase the buffer size for you.

Size that, by the way, you can configure in builder. This is good because it costs a little (in terms of performance) for Java to resize the buffer.

  • 1

    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.

13

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.

6

This is related to the standard sizes and the way the internal buffer is incremented, both for the StringBuffer how much for the StringBuilder.

You can see that because you’re inspecting the field value object, where the string is built and stored. Note, anyway, that the actual size of your string is kept in the count.

5

Just to add, you look in class StringBuilder is like this:

/**
 * Constructs a string builder that contains the same characters
 * as the specified <code>CharSequence</code>. The initial capacity of
 * the string builder is <code>16</code> plus the length of the
 * <code>CharSequence</code> argument.
 *
 * @param      seq   the sequence to copy.
 * @throws    NullPointerException if <code>seq</code> is <code>null</code>
 */
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

public StringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

public StringBuilder append(String str) {
    super.append(str);
    return this;
}

Note that in the constructor it leaves 16 characters blank initially, exactly the amount of whitespace that is after the Eduardo, in your example. This is the size that is left as buffer by default, but as stated in another reply you can pass the initial size on StringBuffer or StringBuilder.

This remaining space makes that the size does not necessarily keep changing with each insertion of values in its object, and at the same time does not have such a large surplus to the point of consuming a significant space of memory.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.