JDK源码解析之StringBuilder和StringBuffer

以下解析基于JDK8.0

二者的继承层次是一样的:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
都是final类,不能再派生子类,这一点跟String类一样。二者底层都是用字符数组char[]实现的,跟String不同的是,该字符数组并没有用final修饰,因此是可变的。二者的绝大多数方法和用来存储字符串的char[]都是来自它们的父类AbstractStringBuilder:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;
StringBuilder和StringBuffer最大的区别就是前者是非线程安全的,后者是线程安全的,体现在代码上就是StringBuffer中的方法绝大多数都比StringBuilder中的方法多了一个synchronized修饰符,比如某一append方法:
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
从上面也可以看出都委托给了父类AbstractStringBuilder类的append方法。

StringBuffer比StringBuilder多了一个toStringCache字段

    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;
这个字段是在调用StringBuffer的toString方法时作为缓存使用的。我们可以看下StringBuffer中的toString方法是如何使用这个字段的:
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
可以看出,如果多次连续调用toString方法的时候由于这个字段的缓存就可以少了Arrays.copyOfRange的操作。(每次调用其他的修改StringBuffer对象的方法时,这些方法的第一步都会先将toStringCache设置为null,如上面的append方法等等。)new String中的第二个参数true代表每次调用toString方法返回的String对象共享toStringCache的值,我们可以看一下String类中的这个构造方法:
    /*
    * Package private constructor which shares value array for speed.
    * this constructor is always expected to be called with share==true.
    * a separate constructor is needed because we already have a public
    * String(char[]) constructor that makes a copy of the given char[].
    */
    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }
注释中也提到了shares value array for speed。

对于append方法,如果append的参数是一个空指针的话则会添加'n','u','l','l'四个字符:

    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;
    }
StringBuilder和StringBuffer默认的初始化容量都是16
    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }
    /**
     * Constructs a string buffer with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuffer() {
        super(16);
    }
    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
也可以在构造函数中指定初始化的容量大小:
    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
如果用指定的String对象进行初始化则是首先将字符串的长度+16作为初始化的容量大小,再append该字符串:
    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
StringBuffer与该方法一样。

二者的扩容机制也是一样的,每次需要扩容的时候都是按照"旧容量*2+2"进行扩容,如果扩容之后仍不满足所需容量,则直接扩容到所需容量

    /**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    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);
    }
总结:

StringBuilder和StringBuffer的相同点:

1、继承层次相同;

2、底层都是用字符数组实现,字符串都是可变的;

3、都是将大部分操作委托给父类;

4、初始容量都是16和扩容机制都是"旧容量*2+2"

不同点:

1、StringBuilder不是线程安全的,StringBuffer是线程安全的

2、StringBuffer从JDK1.0就有了,StringBuilder是JDK5.0增加的

3、StringBuffer比StringBuilder多了一个toStringCache字段,用来在toString方法中进行缓存

4、由于StringBuilder没有考虑同步,在不会出现线程安全问题的情况下,性能上StringBuilder应该要优于StringBuffer

特殊点:

append一个空指针的时候会添加’n','u','l','l'四个字符,长度也同样增加4。

你可能感兴趣的:(JAVA)