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.