StringBuilder and StringBuffer are two commonly used classes for manipulating strings. As we all know, StringBuilder is thread-insecure, while StringBuffer is thread-safe. The former was added by JDK1.5, and the latter was available in JDK1.0. Let’s analyze their internal implementations below.
1. Inheritance relationship
public final class StringBufferextends AbstractStringBuilderimplements java.io.Serializable, CharSequencepublic final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequence
It can be seen that the inheritance relationship between the two classes is exactly the same. Serializable is a serializable flag. The CharSequence interface includes charAt(), length(), subSequence(), and toString() methods. The String class also implements this interface. The focus here is on the abstract class AbstractStringBuilder, which encapsulates the implementation of most operations of StringBuilder and StringBuffer.
2. AbstractStringBuilder
1. Variables and construction methods
char[] value;int count;AbstractStringBuilder() {}AbstractStringBuilder(int capacity) { value = new char[capacity];}AbstractStringBuilder uses a char[] array to save strings, and the initial capacity method can be specified during construction.
2. Expand capacity
public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity);} private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity);}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 expansion method is ultimately implemented by expandCapacity(). In this method, the capacity is first expanded to the original capacity plus 2. If it is still less than the specified capacity at this time, then the new capacity is set to minimumCapacity. Then determine whether it is overflowing. If it is overflowing, set the capacity to Integer.MAX_VALUE. Finally, copying the value value is obviously a time-consuming operation.
3. Append() method
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; }append() is the most commonly used method, it has many forms of overloading. The above is one of them, which is used to append strings. If str is null, the appendNull() method will be called. This method actually adds characters such as 'n', 'u', 'l', and 'l'. If it is not null, first expand the capacity, and then call String's getChars() method to append str to the end of the value. Finally, the object itself is returned, so append() can be called continuously.
3. StringBuilder
AbstractStringBuilder has implemented most of the required methods, and StringBuilder and StringBuffer only need to be called. Let’s take a look at the implementation of StringBuilder.
1. Constructor
public StringBuilder() { super(16);}public StringBuilder(int capacity) { super(capacity);}public StringBuilder(String str) { super(str.length() + 16); append(str);}public StringBuilder(CharSequence seq) { this(seq.length() + 16); append(seq);}As can be seen, the default capacity size of StringBuilder is 16. Of course, you can also specify the initial capacity, or assign the initial value to the StringBuilder object in an existing sequence of characters.
2. Append() method
public StringBuilder append(String str) { super.append(str); return this;}public StringBuilder append(CharSequence s) { super.append(s); return this;}There are many overloading methods for append(), and here are two of them. Obviously, here is the method in the parent class AbstractStringBuilder that is called directly.
3. toString()
public String toString() { // Create a copy, don't share the array return new String(value, 0, count);}The toString() method returns a new String object that does not share memory with the original object. In fact, the same is true for the subString() method in AbstractStringBuilder.
4. SringBuffer
StiringBuffer is similar to StringBuilder, but in order to achieve synchronization, many methods use lSynchronized modification, such as the following method:
public synchronized int length() { return count;}public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this;}public synchronized void setLength(int newLength) { toStringCache = null; super.setLength(newLength);} As you can see, Synchronized is indeed added in front of the method.
In addition, there is also a variable toStringCache in the append() and setLength() methods above. This variable is used for the last cache of the toString() method. Any time the StringBuffer is modified, this variable will be assigned to null. The toString of StringBuffer is as follows:
public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true);}In this method, if toStringCache is null, it is cached first. The final returned String object is a bit different, and this constructor has a parameter true. Find the source code of String and take a look:
String(char[] value, boolean share) { // assert share : "unshared not supported"; this.value = value;}It turns out that the String object constructed by this construction method does not actually copy the string, but only points the value to the construction parameters, which is to save time copying elements. However, this constructor has package access permissions and cannot be called in general.
Summarize
The above is all about this article. I hope it will be helpful for everyone to learn two commonly used operating string classes in Java, StringBuilder and StringBuffer.