This series of articles have been supplemented and improved, and has been revised and organized into a book, The Logic of Java Programming (written by Ma Junchang), published by Huazhang Branch of China Machine Press, which was on the market in January 2018 and received high praise from readers. Major online shops and bookstores are available for sale, welcome to buy: JINGdong self-run link
The StringBuilder and StringBuffer classes are basically the same in their methods and implementation code. The only difference is that StringBuffer is thread-safe. StringBuilder doesn’t.
The concept of threads and thread safety will be discussed in more detail in a later section. The important thing to know here is that thread-safety comes at a cost and affects performance, while string objects and operations, for the most part, are not thread-safe and are suitable for Using StringBuilder. So, this section will focus on StringBuilders.
The basic use of StringBuilder is pretty simple, so let’s look at it.
Basic usage
Create a StringBuilder
StringBuilder sb = new StringBuilder();
Copy the code
Add string via append method
sb.append("Old Horse says programming.");
sb.append("Exploring the nature of programming");
Copy the code
Gets the constructed string, using the toString method
System.out.println(sb.toString());
Copy the code
The output is:
Ma said programming, explore the nature of programmingCopy the code
For the most part, it’s as simple as creating a New StringBuilder through New, adding a string through AppEnd, and getting the completed string through toString.
How does StringBuilder work?
Basic Implementation Principles
Internal composition and construction method
Like String, the StringBuilder class encapsulates an array of characters, defined as follows:
char[] value;
Copy the code
Unlike String, it is not final and can be modified. In addition, unlike String, not all positions in a character array are already used. It has an instance variable that represents the number of characters already used in the array, defined as follows:
int count;
Copy the code
AbstractStringBuilder is derived from AbstractStringBuilder, and its default constructor is:
public StringBuilder(a) {
super(16);
}
Copy the code
Call the constructor of the parent class, which corresponds to:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
Copy the code
In other words, the new StringBuilder() code internally creates an array of characters of length 16, and the default value of count is 0.
Append the implementation of the
Look at the code for append:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
Copy the code
Append copies characters directly into the internal character array. If the length of the character array is insufficient, appEnd extends it. The actual length used is represented by count. Specifically, ensureCapacityInternal(count+len) ensures that the array is long enough to hold the newly added character, str.getchars copies the newly added character into the character array, and count+=len increases the actual length used.
The code of ensureCapacityInternal is as follows :(if you use the gold digging app to view, there may be garbled code, is the gold digging bug, can be viewed by the gold digging PC version, or follow my wechat public account “old ma said programming” to view)
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
Copy the code
If the length of the character array is smaller than the required length, the extension is called expandCapacity. The code for expandCapacity is:
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);
}
Copy the code
The extension logic is to allocate a new array of sufficient length, copy the original contents into the new array, and then make the internal character array point to the new array. This logic is implemented with the following code:
value = Arrays.copyOf(value, newCapacity);
Copy the code
In the next video, we’ll talk about the Arrays class, but we’ll skip this section and just look at how newCapacity is computed.
The minimumCapacity parameter indicates the minimum length required. No, because that would be the same as String, which allocates memory every append, which is inefficient. The expansion strategy here is related to the current length, the current length times 2, plus 2, if this length is not the minimum required length, use minimumCapacity.
For example, the default length is 16. If the length is insufficient, it is first extended to 16*2+2 (34), then to 34*2+2 (70), and then to 70*2+2 (142), which is an exponential scaling strategy. Why do I add 2? Probably because it works just as well at length zero.
Why do you want to expand this? This is a trade-off between reducing the number of memory allocations and avoiding space waste. Exponential scaling is a common strategy used in all kinds of memory-allocation related computer programs without knowing how long it will ultimately take.
What if you knew in advance how long it would take? Another constructor of StringBuilder can be called:
public StringBuilder(int capacity)
Copy the code
The toString implementation
With the string constructed, let’s look at the toString code:
public String toString(a) {
// Create a copy, don't share the array
return new String(value, 0, count);
}
Copy the code
Create a new String based on the internal array. Note that the String constructor does not use the value array directly, but creates a new String to ensure that the String is immutable.
More constructors and append methods
String also has two constructors that take the String and CharSequence arguments, and their code is as follows:
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
Copy the code
The logic is simple: allocate an extra 16 characters of space and then call Append to add the parameter characters.
Append can take various types of arguments, convert them to characters, and add them to an append. These methods include:
public StringBuilder append(boolean b)
public StringBuilder append(char c)
public StringBuilder append(double d)
public StringBuilder append(float f)
public StringBuilder append(int i)
public StringBuilder append(long lng)
public StringBuilder append(char[] str)
public StringBuilder append(char[] str, int offset, int len)
public StringBuilder append(Object obj)
public StringBuilder append(StringBuffer sb)
public StringBuilder append(CharSequence s)
public StringBuilder append(CharSequence s, int start, int end)
Copy the code
The concrete implementation is relatively straightforward, so I don’t want to repeat it.
There is also an append method to add a Code Point:
public StringBuilder appendCodePoint(int codePoint)
Copy the code
If codePoint is a BMP character, add one char, otherwise add two char. If the concept of Code Point is not clear, see Dissecting wrapped Classes (below).
Other Modification methods
In addition to append, StringBuilder has a few other modifications, so let’s take a look.
insert
public StringBuilder insert(int offset, String str)
Copy the code
Insert the string STR at the specified index offset, with offset 0 to start and length() to end, for example:
StringBuilder sb = new StringBuilder();
sb.append("Old Horse says programming.");
sb.insert(0."Attention");
sb.insert(sb.length(), "Ma and you explore the nature of programming.");
sb.insert(7.",");
System.out.println(sb.toString());
Copy the code
The output is
Pay attention to old ma said programming, old ma and you explore the nature of programmingCopy the code
Take a look at the insert implementation code:
public AbstractStringBuilder insert(int offset, String str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
if (str == null)
str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(value, offset);
count += len;
return this;
}
Copy the code
The idea is that, after ensuring a sufficient length, the array begins with n positions at offset, where n is the length of the string to be inserted, and then copies the string into offset.
The System. Arraycopy method is called instead. This is a common method that declares:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
Copy the code
Copy the length element starting from srcPos in SRC to destPos in dest. The advantage of this method is that it works even if SRC and dest are the same array. For example, look at the following code:
Int [] arr = new int[]{1,2,3,4}; System.arraycopy(arr, 1, arr, 0, 3); System.out.println(arr[0]+","+arr[1]+","+arr[2]);
Copy the code
SRC and dest are arr, srcPos is 1, destPos is 0, length is 3, which means that the first three elements are moved to the beginning of the second element.
2 and 4Copy the code
The declaration of arraycopy has a modifier native, indicating that its implementation is implemented through the Java native interface, a technology provided by Java for calling code in Java that is not implemented in the Java language. In fact, arraycopy is implemented in C++. Why C++? Because this feature is so common, C++ is much more efficient than Java.
Other insertion methods
Like Append, INSERT has a number of overloaded methods, listed below
public StringBuilder insert(int offset, double d)
public StringBuilder insert(int offset, Object obj)
Copy the code
delete
Deletes characters in the specified range
public StringBuilder delete(int start, int end)
Copy the code
Its implementation code is as follows:
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
Copy the code
It is also implemented through System. arrayCopy, which is used extensively in the internal implementation of StringBuilder and won’t be covered here.
Delete a character
public StringBuilder deleteCharAt(int index)
Copy the code
replace
public StringBuilder replace(int start, int end, String str)
Copy the code
Such as
StringBuilder sb = new StringBuilder();
sb.append("Old Horse says programming.");
sb.replace(3.5."Java");
System.out.println(sb.toString());
Copy the code
The program output is:
He said the JavaCopy the code
Replace a character
public void setCharAt(int index, char ch)
Copy the code
Flip string
public StringBuilder reverse(a)
Copy the code
Instead of simply flipping the char in the array, this method ensures that the char is still valid by checking the char separately and flipping it twice. Such as:
StringBuilder sb = new StringBuilder();
sb.append("a");
sb.appendCodePoint(0x2F81A);// Additional characters: winter
sb.append("b");
sb.reverse();
System.out.println(sb.toString());
Copy the code
The output is correct even if the supplementary character “winter” is included:
Winter is a bCopy the code
The length of the method
StringBuilder has some length related methods
Ensure that the character array length is not less than the given value
public void ensureCapacity(int minimumCapacity)
Copy the code
Returns the length of the character array
public int capacity(a)
Copy the code
Returns the actual length used by the array
public int length(a)
Copy the code
Note the difference between the capacity() method and the length() method. Capacity returns the length of the value array, while length returns the actual number of characters used and the value of the count instance variable.
Modify length directly
public void setLength(int newLength)
Copy the code
The code is:
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
ensureCapacityInternal(newLength);
if (count < newLength) {
for (; count < newLength; count++)
value[count] = '\ 0';
} else{ count = newLength; }}Copy the code
Count is set to newLength. If count is less than newLength, the default value for extra characters is ‘\0’.
Reduce the space you use
public void trimToSize(a)
Copy the code
The code is:
public void trimToSize(a) {
if(count < value.length) { value = Arrays.copyOf(value, count); }}Copy the code
Reduce the space occupied by value and create a new space that is just enough.
A method similar to String
StringBuilder also has some methods similar to String, such as:
Find substrings
public int indexOf(String str)
public int indexOf(String str, int fromIndex)
public int lastIndexOf(String str)
public int lastIndexOf(String str, int fromIndex)
Copy the code
Take substring
public String substring(int start)
public String substring(int start, int end)
public CharSequence subSequence(int start, int end)
Copy the code
Gets the character or Code Point
public char charAt(int index)
public int codePointAt(int index)
public int codePointBefore(int index)
public int codePointCount(int beginIndex, int endIndex)
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
Copy the code
These methods are basically the same as in String, so I won’t go over them in this section.
String + and += operators
In Java, strings can use the + and += operators directly, which is supported by the Java compiler. Behind the scenes, the Java compiler generates StringBuilder, and the + and += operations are converted to Append. For example, the following code:
String hello = "hello";
hello+=",world";
System.out.println(hello);
Copy the code
Behind the scenes, the Java compiler converts to:
StringBuilder hello = new StringBuilder("hello");
hello.append(",world");
System.out.println(hello.toString());
Copy the code
Since using + and += directly is equivalent to using StringBuilder and AppEnd, why use StringBuilder directly? In simple cases, it’s not really necessary. However, in slightly more complex cases, where the Java compiler is not as smart, it may generate a lot of StringBuilders, especially if there are loops, such as the following code:
String hello = "hello";
for(int i=0; i<3; i++){ hello+=",world";
}
System.out.println(hello);
Copy the code
The converted Java compiler code looks something like this:
String hello = "hello";
for(int i=0; i<3; i++){ StringBuilder sb =new StringBuilder(hello);
sb.append(",world");
hello = sb.toString();
}
System.out.println(hello);
Copy the code
Inside the loop, a StringBuilder is generated for each += operation.
So, the conclusion is that you can use String + and += directly for simple cases, and you should use StringBuilder directly for complex cases, especially if there are loops.
summary
This section introduces StringBuilder, its usage, implementation, array length extension strategy, and implementation of the String + and += operators.
String manipulation is one of the most common operations in computer programs. By understanding the usage and implementation of String and StringBuilder, we have established a solid foundation for String manipulation.
In both the previous section and this section, we talked about a class called Arrays that contains a lot of array-related methods. Array manipulation is also very common, so let’s discuss it in detail in the next section.
To be continued, check the latest articles, please pay attention to the wechat public account “Lao Ma said programming” (scan the qr code below), simple and simple, Lao Ma and you explore the nature of Java programming and computer technology. All rights reserved.