Overview
StringBuilder and StringBuffer are two easily confusing concepts. This article starts from the source code and simply looks at the similarities and differences between the two.
It is easy to know that one of these two is thread-safe, and the thread-safe one is inefficient.
Description in java doc
Java doc is an annotation written by people who write source code. Let’s look at Java doc first.
StringBuilder
A mutable sequence of characters. This class provides an API compatible with StringBuffer, but with no guarantee of synchronization. This class is designed for use as a drop-in replacement for StringBuffer in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.
The principal operations on a StringBuilder are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string builder. The append method always adds these characters at the end of the builder; the insert method adds the characters at a specified point.
For example, if z refers to a string builder object whose current contents are "start", then the method call z.append("le") would cause the string builder to contain "startle", whereas z.insert(4, "le") would alter the string builder to contain "starlet".
In general, if sb referers to an instance of a StringBuilder, then sb.append(x) has the same effect as sb.insert(sb.length(), x).
Every string builder has a capacity. As long as the length of the character sequence contained in the string builder does not exceed the capacity, it is not necessary to allocate a new internal buffer. If the internal buffer overflows, it is automatically made larger.
Instances of StringBuilder are not safe for use by multiple threads. If such synchronization is required then it is recommended that java.lang.StringBuffer be used.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
Since:
1.5
Author:
Michael McCloskey
See Also:
java.lang.StringBuffer
java.lang.String
StringBuffer
A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.
String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.
The principal operations on a StringBuffer are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The append method always adds these characters at the end of the buffer; the insert method adds the characters at a specified point.
For example, if z refers to a string buffer object whose current contents are "start", then the method call z.append("le") would cause the string buffer to contain "startle", whereas z.insert(4, "le") would alter the string buffer to contain "starlet".
In general, if sb referers to an instance of a StringBuffer, then sb.append(x) has the same effect as sb.insert(sb.length(),
Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence), this class synchronizes only on the string buffer performing the operation, not on the source. Note that while StringBuffer is designed to be safe to use concurrently from multiple threads, if the constructor or the append or insert operation is passed a source sequence that is shared across threads, the calling code must ensure that the operation has a consistent and unchanging view of the source sequence for the duration of the operation. This could be satisfied by the caller holding a lock during the operation's call, by using an immutable source sequence, or by not sharing the source sequence across threads.
Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.
Since:
JDK1.0
Author:
Arthur van Hoff
See Also:
java.lang.StringBuilder
java.lang.String
javadoc summary
From the above, we can see:
Both StringBuffer and StringBuilder can be considered variable Strings.
StringBuffer is thread-safe and appears first, and it is available in JDK1.0.
StringBuilder is non-thread-safe and appears later and only exists in JDK1.5.
The interfaces of the two are exactly the same, and StringBuilder is faster.
In fact, it’s good to use it normally, just know these points, but I still want to see how it is implemented in the source code.
Source code
How to achieve thread safety
You can see from the source code that both inherit an abstract class AbstractStringBuilder
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequencepublic final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence
The amount of code is not very large, StringBuilder440 lines of code, StringBuffer718 lines of code, and the most is AbstractStringBuilder, with a total of 1440 lines of code.
From several perspectives, we look at the code, one is what some key internal structures look like, and the other is how the functions we commonly use are implemented. Because String is immutable, it is placed in a constant pool. It is guessed that StringBuilder and StringBuffer should be implemented using a char array.
/** * The value is used for character storage. */ char[] value; /** * The count is the number of characters used. */ int count;
It can be seen that the data is stored with value and the length is represented by a count.
Let’s see the different implementations of several common methods.
StringBuffer
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ @Override public synchronized StringBuffer insert(int offset, String str) { toStringCache = null; super.insert(offset, str); return this; } @Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }StringBuilder
@Override public StringBuilder append(String str) { super.append(str); return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ @Override public StringBuilder insert(int offset, String str) { super.insert(offset, str); return this; } @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }As can be seen from the code, in most cases, StrinbBuffer just adds a synchronized keyword to ensure thread safety. But the toString method is different, so I will talk about this later.
Initialize size and how to grow
Since it is actually an array, the size and growth method of the array starting from is very important. Let's take a look through the code.
/** * Constructs a string buffer with no characters in it and an * initial capacity of 16 characters. */ public StringBuffer() { super(16); } /** * Constructs a string builder with no characters in it and an * initial capacity of 16 characters. */ public StringBuilder() { super(16); }As you can see, both of these default constructors indicate that the default array size is 16.
Why 16? I didn't understand.
What's next to worry about how to grow? Let's take a look at the implementation of append
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }The above three methods explain how to expand capacity.
Put the current capacity *2+2
If the newly added length is greater than this value, set to the newly added value
If overflow, throw OutOfMemoryError
Implementation of toString in StringBuffer
/** * A cache of the last value returned by toString. Cleared * whenever the StringBuffer is modified. */ private transient char[] toStringCache; @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } @Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }As you can see, an array toStringCache is defined. Each time the data changes, this is set to null. When toString, take it again from the current data.
The transient keyword is to prevent this array from being serialized.
summary
In fact, the source code of Java itself is relatively simple. If you can start with the source code, you can understand many principles more deeply. This article simply lists some source codes and briefly explains the similarities and differences between StringBuffer and StringBuilder. Friends who are interested can take a look at it themselves.
The above article briefly looks at the similarities and differences between StringBuilder and StringBuffer from the source code perspective (comprehensive analysis) is all the content I share with you. I hope it can give you a reference and I hope you can support Wulin.com more.