StringBuffe source code analysis and StringBuilder


StringBuffer and StringBuilder are two often used to do string manipulation classes, people usually know StringBuffer is thread-safe, StringBuilder is not thread-safe, but for its internal source did not do too much analysis, we analyze two those differences and their advantages and disadvantages source.
The starting point of this paper is to proceed from the source code, detailed analysis of the difference between the two, both general conclusions about before has written an article summary: String, StringBuffer and StringBuilder difference

1. StringBuffer

Overall String, StringBuffer, StringBuilder class structure between three classes:
Here Insert Picture Description
the above class structure in FIG, either directly or indirectly dependent on dependency, and the StringBuffer StringBuilder Appendable and interfaces are based CharSequence basis function expansion down to do, because string itself are not changed, and the StringBuffer StringBuilder order to more convenient and efficient operation of the string, the string operations to achieve many of the features, such as appending the string, the string is inserted, and so replacement string.

1.1 source code analysis

According to the above structure analysis StringBuffer class inherited the main abstract class and interface CharSequence AbstractStringBuilder, which provides a method of operation of some primary AbstractStringBuilder string, can be divided into functionally graded appending the string, delete characters, replacing the characters, inserting characters, the character indexes acquired string flips, the series methods are the core of the operating method of the string, and the methods to achieve CharSequence interface primarily get string length, to obtain an index of the character.
Note: All methods are thread-safe, because each method are modified with the synchronized keyword, the more weight class belongs to a lock, the lock according to the characteristics, performance is bound to be lower.
StringBuffer whole property inherited from AbstractStringBuilder, while AbstractStringBuilder underlying character array is used to store characters.

  1. The use of an array of characters stored in the string value
    char[] value;
  2. count the number of characters in the real value stored in the array
    int count;

According to the following function point source comb

1.1.1 Constructor

1. Empty StringBuffer object configuration

The method is mainly AbstractStringBuilder constructor call parent class, to establish a string buffer sequence does not comprise any string, the default buffer size is created 16
StringBuffer.java

public StringBuffer() {
    super(16);
}

AbstractStringBuilder.java

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

2. StringBuffer object size specified configuration

Create a does not contain any string of StringBuffer object, given a particular string buffer size, and the parent class's method is similar to the call by the super ().

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

3. The configuration of the specified initial string StringBuffer object

The specific string objects StringBuffer object configured to, create the default size of argument string length StringBuffer +16

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

Which by append () method to store the string to the original array, note that this method coupled with the synchronized keyword, by locking way to ensure the safety of this method among multiple threads , and the internal implementation method is by the parent class AbstractStringBuilder append () method. From here you can see the importance of AbstractStringBuilder basically is the core method of StringBuffer class.

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

Call append parent class ()

// 该方法是父类AbstractStringBuilder的共用方法
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    // 用于数组长度的扩展
    ensureCapacityInternal(count + len);
    // 将字符串str追加到value字符数组后
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

Array expansion method to analyze:

// 数组扩展的方法
// 该方法是ensureCapacity的非同步版本
// 主要目的将数组扩展至minimumCapacity,主要是利用数组复制的方式
private void ensureCapacityInternal(int minimumCapacity) {
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,newCapacity(minimumCapacity));
    }
}


/**
 * 扩展数组大小的思路
 * 1. 判断参数中的minCapacity和(2*nowCapacity+2) = newCapacity的大小
 * 2. 如果newCapacity < minCapacity,则将数组扩展为minCapacity
 * 3. 如果newCapacity > minCapacity,则将数组扩展为2 * nowCapacity + 2
 * 4. 校验最新扩容的大小,至少不能比Integer.MAX_VALUE大
 * @param minCapacity
 * @return
 */
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;
}

/**
 * 校验最新扩容的大小,防止数据溢出
 * @param minCapacity
 * @return
 */
private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { 
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

4. The configuration of the specified initial string StringBuffer object

String length sequence of characters into a string configured, the default size parameters created StringBuffer +16
specific extension length of the array and construction methods analogous 3

public StringBuffer(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

1.1.2 The basic method

A method in accordance with the basic function will be to distinguish between the various methods, for controlling the profile analysis, each feature is only one method of analysis.

1. Adding character welfare

Source code is available in a variety of ways append strings, but overall similar, but different processing request parameters only, now only be analyzed for the most common request parameters Object
Here Insert Picture Description

/**
 * 追加字符串
 * 该方法继承于AbstractStringBuilder
 * 思路:
 * 1. 将参数做强制类型转化为String,再通过父类AbstractStringBuilder的append()方法追加字符串
 * @param   obj   an {@code Object}.
 * @return
 */
@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    // 调用父类的append()方法追加字符串
    super.append(String.valueOf(obj));
    return this;
}

2. Remove the string

Delete character within the specified range of indices, there is a method to delete the specified index character, consistent whole idea.

/**
 * @param      start  开始的下标索引
 * @param      end    结束的下标索引
 * @return
 */
@Override
public synchronized StringBuffer delete(int start, int end) {
    toStringCache = null;
    // 通过调用父类的删除字符串方法
    super.delete(start, end);
    return this;
}


/**
 * 删除字符序列中一部分
 * 思路:
 * 1. 前面的都是一些参数的校验,比如检查收尾索引是否得当
 * 2. 通过数组复制的方式删除元素
 * @param start
 * @param end
 * @return
 */
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;
}	

3. Obtain the part of the string

/**
 * 获取字符串的一部分
 * @param      start    开始索引
 * @param      end      结束索引
 * @return
 */
@Override
public synchronized String substring(int start, int end) {
    return super.substring(start, end);
}

/**
 * 获取字符串一部分
 * 前面是参数的校验,真正的思路是通过String的构造方法,构造出一个新的字符串
 * @param start
 * @param end
 * @return
 */
public String substring(int start, int end) {
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (end > count)
        throw new StringIndexOutOfBoundsException(end);
    if (start > end)
        throw new StringIndexOutOfBoundsException(end - start);
    return new String(value, start, end - start);
}

4. Insert Character

Here Insert Picture Description
Although there are many methods, but only one analysis method, with a way to understand the idea of ​​the entire insert.

/**
 * 将字符串插入到元字符串中
 * @param      offset   开始插入的索引下标
 * @param      obj      插入的元素
 * @return
 */
@Override
public synchronized StringBuffer insert(int offset, Object obj) {
    toStringCache = null;
    // 调用父类的insert()方法
    super.insert(offset, String.valueOf(obj));
    return this;
}

/**
 * 将字符串插入的原字符索引中
 * 思路:
 * 1. 扩容
 * 2. 复制数组的方式扩展字符数组
 * @param offset        开始索引
 * @param str           插入的字符串
 * @return
 */
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;
}

5. Get subscripting

Get string str start position in the original string, the method asynchronous method.
In this method, a multi-layer calls, StringBuffer -> AbstractStringBuilder -> String

@Override
public int indexOf(String str) {
    return super.indexOf(str);
}
/**
 * 该方法主要用于String、StringBuffer中的搜索使用,
 * @param source            被搜索的字符串
 * @param sourceOffset      被搜索字符的下标
 * @param sourceCount       被搜索字符串的总字符数
 * @param target            搜索的字符串
 * @param targetOffset      搜索字符串下标
 * @param targetCount       搜索字符串总字符数
 * @param fromIndex         开始搜索的下标
 * @return  返回目标字符串的起始下标,如果没有则返回-1
 */
static int indexOf(char[] source, int sourceOffset, int sourceCount,
        char[] target, int targetOffset, int targetCount,
        int fromIndex) {
    if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
    }
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    if (targetCount == 0) {
        return fromIndex;
    }

    char first = target[targetOffset];
    int max = sourceOffset + (sourceCount - targetCount);

    /**
     * 整体思路:通过双层循环方式寻找目标字符串的开始及结束
     */
    for (int i = sourceOffset + fromIndex; i <= max; i++) {
        /* Look for first character. */
        if (source[i] != first) {
            while (++i <= max && source[i] != first);
        }

        /* Found first character, now look at the rest of v2 */
        if (i <= max) {
            int j = i + 1;
            int end = j + targetCount - 1;
            for (int k = targetOffset + 1; j < end && source[j]
                    == target[k]; j++, k++);

            if (j == end) {
                /* Found whole string. */
                return i - sourceOffset;
            }
        }
    }
    return -1;
}

6. Flip string

Source in accordance with the analysis, the time complexity of the process itself is O (log2N)

/**
 * 字符串翻转
 * @return
 */
@Override
public synchronized StringBuffer reverse() {
    toStringCache = null;
    // 通过调用父类的字符串翻转方法
    super.reverse();
    return this;
}


/**
 * 翻转字符串
 * 整体的时间复杂度:O(log2N)
 * @return
 */
public AbstractStringBuilder reverse() {
    boolean hasSurrogates = false;
    int n = count - 1;
    for (int j = (n-1) >> 1; j >= 0; j--) {
        int k = n - j;
        char cj = value[j];
        char ck = value[k];
        value[j] = ck;
        value[k] = cj;
        if (Character.isSurrogate(cj) ||
            Character.isSurrogate(ck)) {
            hasSurrogates = true;
        }
    }
    if (hasSurrogates) {
        reverseAllValidSurrogatePairs();
    }
    return this;
}

Capacity length and character character array 7.

See the source code provides two methods length () and capacity () method, the former is the number of characters get real StringBuffer object storage, the real capacity of the array StringBuffer object of the latter acquired.

/**
 * 获取StringBuffer对象字符串的总长度
 * @return
 */
@Override
public synchronized int length() {
    return count;
}
/**
 * 表示字符数组中的容量,一般该方法获取的值>=length()获取的值
 * @return
 */
@Override
public synchronized int capacity() {
    return value.length;
}

example:

public class TestStringBuffer {
    public static void main(String[] args) {
    	// 该构造方法会默认创建长度为26的字符数组
        StringBuffer s = new StringBuffer("helloWorld");
        System.out.println(s.length());         // 10
        System.out.println(s.capacity());       // 26
    }
}

1.1.3 StringBuffer summary

You can see from the above method, the method in this class are basically adding a thread-safe multi-thread between the Synchronized keyword for guarantee.

2. StringBuilder

This article does not do a detailed analysis of the source code, because the code itself and StringBuffer similar, but each method has no StringBuilder plus Synchronized, although the loss of synchronization features, but greatly improved performance, the frequency of use is also generally larger than StringBuffer.

Published 97 original articles · won praise 19 · views 80000 +

Guess you like

Origin blog.csdn.net/weixin_37690143/article/details/104184646