JavaSE-String, StringBuilder and StringBuffer source code analysis

table of Contents

Introduction

String

Inheritance

Member variables and construction methods

StringBuilder和StringBuffer

Inheritance

AbstractStringBuilder

Member variables and construction methods

Expansion method

append () method

StringBuilder

Construction method

append () method

toString () method

StringBuffer

Member variables and construction methods

append () method

toString () method

to sum up


Introduction


Under JDK1.8, String, StringBuilder and StringBuffer are commonly used to manipulate strings. String is an immutable sequence of characters, and StringBuilder and StringBuffer are variable sequences of characters. StringBuilder is thread-unsafe, String and StringBuilder are thread-safe. Let's analyze their internal implementation.

String


Inheritance


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

As you can see, String implements the Serializable , Comparable, and CharSequence interfaces. Serializable is a serializable mark. The Comparable interface contains the compareTo () method. The CharSequence interface contains charAt () , length () , subSequence () and toString () methods.

Member variables and construction methods


private final char value[];

private int hash; // Default to 0

public String() {
    this.value = "".value;
}

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

String uses a constant char [] array to store the string, which is immutable. Char [] array is converted to String type, which is a deep copy through the Arrays copyOff () method, copying the local char [] array and assigning it to the constant char [] array.

StringBuilder和StringBuffer


Inheritance


public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

 

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

It can be seen that the classes and interfaces implemented by StringBuilder and StringBuffer are exactly the same, and the Serializable and CharSquence interfaces are implemented like String . The most important thing is that the inherited AbstractStringBuilder abstract class encapsulates most of the methods of StringBuilder and StringBuffer.

AbstractStringBuilder


Member variables and construction methods


char[] value;

int count;

AbstractStringBuilder() {
}

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

AbstractStringBuilder also defines a char [] array to store the string, but it is not modified with final , so the string is variable. At the same time, a global variable cout of type int is also defined . When the configuration can specify the initial capacity Capacity , in order to obtain better performance.

Expansion method


public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
            newCapacity(minimumCapacity));
    }
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

The expansion method of AbstractStringBuilder is implemented by newCapacity () . In this method, the capacity is expanded to twice the original capacity (shift operation is used instead of *), and 2 is added. If the expanded capacity is less than the specified capacity, set minCapacity to the latest capacity. If the maximum value of Integer ( 0x7fffffff ) minus 8 is less than the latest capacity, call the hugeCapacity () method. To determine whether it overflows, throw an exception OutOfMemoryError if it overflows, otherwise, if the new capacity is greater than the maximum value of Integer minus 8, set the capacity to minCapacity , otherwise set the maximum value of Integer minus 8. If the maximum value of Integer ( 0x7fffffff ) minus 8 is greater than the latest capacity, otherwise the capacity is set to newCapacity .

append () method


public AbstractStringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

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;
}

private AbstractStringBuilder appendNull() {
	int c = count;
	ensureCapacityInternal(c + 4);
	final char[] value = this.value;
	value[c++] = 'n';
	value[c++] = 'u';
	value[c++] = 'l';
	value[c++] = 'l';
	count = c;
	return this;
}

The append () method is the most commonly used method of StringBuilder and StringBuffer, overloading many methods for appending strings. The first append () method converts the obj object to String type and appends it through the String's valueOf () method . The second append () method, if str is null , then the call appendNull () method, first expansion ( raw capacity plus 4 ), then an additional 'n-' , 'U' , 'L' , 'L' of the four character. If str is not null , first expansion ( raw capacity plus the length of the string ), then call String of getChars () method, str added to the char [] array value at the end, and finally the capacity toOriginal capacity + string length , return the object itself, so that you can continuously call the append () method.

StringBuilder


Construction method


public StringBuilder() {
	super(16);
}

public StringBuilder(int capacity) {
	super(capacity);
}

public StringBuilder(String str) {
	super(str.length() + 16);
	append(str);
}

It can be seen that the default capacity of StringBuilder is 16 , and its initial capacity can also be specified. If you assign a StringBuilder object with a string, the capacity of the StringBuiler at this time is the length of the current string plus 16 .

append () method


@Override
public StringBuilder append(Object obj) {
	return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
	super.append(str);
	return this;
}

The append () method in StringBuilder is a method to rewrite the abstract class AbstractStringBuilder , overloading many append () methods. The implementation of its methods are to call the append () method of the parent class AbstractStringBuilder . When multiple threads access this method, because count is a global variable, when multiple threads execute count + = len , there will be problems with the value of count, resulting in inconsistency with the actual, so the StringBuilder thread is not safe .

toString () method


@Override
public String toString() {
	// Create a copy, don't share the array
	return new String(value, 0, count);
}

StringBuilder rewrites Object 's toString () method, new Stirng () internally calls Arrays ' copyOfRange () method ( deep copy ), returns a new String object, and does not share a memory space with the original object.

StringBuffer


Member variables and construction methods


private transient char[] toStringCache;

public StringBuffer() {
	super(16);
}

public StringBuffer(int capacity) {
	super(capacity);
}

public StringBuffer(String str) {
	super(str.length() + 16);
	append(str);
}

When constructed, the StringBuffer and StringBuilder as default capacity size of 16 , can specify the initial capacity. The only difference is that StringBuffer defines a char [] array toStringCache decorated with transient . transient keyword, when modifying a variable, the variable cannot be serialized.

append () method


@Override
public synchronized StringBuffer append(Object obj) {
	toStringCache = null;
	super.append(String.valueOf(obj));
	return this;
}

@Override
public synchronized StringBuffer append(String str) {
	toStringCache = null;
	super.append(str);
	return this;
}

It can be seen that StringBuffer and StringBuilder rewrite the append () method of the parent class of AbstractStringBuilder , internally implement the append () method of the parent class , and overload many append () methods. The only difference is that each time StringBuffer modifies the data, the char [] array toStringCache will be set to empty, and each method of StringBuffer is preceded by the synchronized keyword. When multiple threads access this method, one thread is accessing this method, and other threads will be locked and cannot access this method. Only when this thread executes this method and releases the lock, other threads can access this method, so StringBuffer Is thread safe .

toString () method


@Override
public synchronized String toString() {
	if (toStringCache == null) {
		toStringCache = Arrays.copyOfRange(value, 0, count);
	}
	return new String(toStringCache, true);
}

String(char[] value, boolean share) {
	// assert share : "unshared not supported";
	this.value = value;
}

StringBuffer 's toString () method is also different from StringBuilder . If toStringCache is null , it is cached first, and finally returns a String object. StringBuffer 's new String () does not call Arrays ' copyOfRange () method inside , just a simple array assignment ( shallow copy ), saving the time of copying elements.

to sum up


  • String is an immutable string, StringBuffer and StringBuilder are variable strings.
  • StringBuilder is thread-unsafe and fast , and StringBuffer is thread-safe and slow .
  • Most methods of StringBuilder and StringBuffer call the implementation of the parent class AbstractStringBuilder. The expansion mechanism is to first increase the capacity to 2 times the original capacity plus 2 . The maximum capacity is the maximum value of Integer (0x7fffffff) minus 8 .
  • The default capacity of StringBuilder and StringBuffer are both 16 , you can specify the size when creating StringBuffer and StringBuilder, so as to avoid automatic growth when the capacity is not enough to improve performance.
Published 131 original articles · won 12 · 60,000 views +

Guess you like

Origin blog.csdn.net/JavaDestiny/article/details/100710981