NOTE

StringBuilder

1. What It Is: a mutable, thread-unsafe string. 2. Usage. 3. Implementation analysis: constructor, append, toString, and substring.

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What It Is

A mutable, thread-unsafe string.

Its implementation is somewhat similar to ArrayList: it uses a char array underneath, and it needs to expand when capacity is insufficient.

2. How to Use It

public class StringBuilderTest
{
    public static void main(String[] args)
    {
        StringBuilder builder = new StringBuilder();
        builder.append("test");
    }
}

3. Implementation Analysis

3.1. Constructor

// Cannot be inherited; serializable
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    public StringBuilder() {
        // Call the parent class (AbstractBuilder) constructor and initialize the capacity to 16
        super(16);
    }
}


// AbstractBuilder constructor
abstract class AbstractStringBuilder implements Appendable, CharSequence {

   // Not modified with final, which means the reference can change
    char[] value;
    int count;
    AbstractStringBuilder() {
    }

    AbstractStringBuilder(int capacity) {
        // Create a character array
        value = new char[capacity];
    }

3.2. append Method

Characters are appended directly to the internal char array.

If the capacity is insufficient, it needs to expand to twice the original capacity plus 2.

public StringBuilder append(String str) {
        // Call the parent class's append method
        super.append(str);
        return this;
    }
  • AbstractBuilder’s append method
public AbstractStringBuilder append(String str) {
        // If it is null, append the string "null"
        if (str == null)
            return appendNull();
        // Capacity must also be sufficient
        int len = str.length();
        ensureCapacityInternal(count + len);
        // String's getChars method. Check whether the parameters are valid, then call System.arraycopy to copy the array
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

 private AbstractStringBuilder appendNull() {
        int c = count;
        // Ensure there is enough capacity for the original capacity + 4
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }
private void ensureCapacityInternal(int minimumCapacity) {
        // overflowconscious code
        // Subtraction is used to prevent overflow
        if (minimumCapacity  value.length > 0) {
            value = Arrays.copyOf(value,
                    // Calculate the expanded capacity
                    newCapacity(minimumCapacity));
        }
    }

private int newCapacity(int minCapacity) {
    // overflowconscious code
    // Original capacity * 2 + 2
    int newCapacity = (value.length << 1) + 2;
    // If original capacity * 2 + 2 < the minimum required capacity, use the minimum required capacity
    if (newCapacity  minCapacity < 0) {
        newCapacity = minCapacity;
    }
    // The calculated new capacity overflows or exceeds the maximum limit: private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE  8
    return (newCapacity <= 0 || MAX_ARRAY_SIZE  newCapacity < 0)
        // Use the minimum required capacity instead
        ? hugeCapacity(minCapacity)
        // Otherwise return it directly
        : newCapacity;
}

private int hugeCapacity(int minCapacity) {
    // If the minimum required capacity exceeds the maximum integer value, throw OOM
    if (Integer.MAX_VALUE  minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    // Take either the minimum required capacity or the maximum limit
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

// String's getChars method
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd  srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd  srcBegin);
    }

3.3. toString

public String toString() {
        // Construct a String object directly
        return new String(value, 0, count);
    }

3.4. subString

public String substring(int start, int end) {
    // Index out-of-bounds check
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (end > count)
        throw new StringIndexOutOfBoundsException(end);
    if (start > end)
        throw new StringIndexOutOfBoundsException(end  start);
    // Construct using String
    return new String(value, start, end  start);
}

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub