「OpenJdk-11 源码-系列」AbstractStringBuilder,StringBuilder,StringBuffer

490 阅读3分钟

AbstractStringBuilder

AbstracStringBuilderStringBuilderStringBuffer 的父类。前面我们讲到String是一个不可变的字符串。而StringBuilderStringBuffer 则是对String的一个补充,它们是可变的。先来看AbstractStringBuilder的定义

定义

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
	int count;
	AbstractStringBuilder() {
	}
	AbstractStringBuilder(int capacity) {
    	value = new char[capacity];
	}
}

从类名上就可以看出来它是一个抽象方法。并且实现了Appendable以及Appendable接口中的append方法。使用了char[]来保存值,而两个构造函数也是子类所必须要实现的,用于初始化 char[]的大小。

扩容

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

对于扩容,需要确保传入的capacity大于目前的value的长度,且小于Integer.MAX_VALUE(0x7fffffff)。在这个方法中首先把容量扩大为原来的容量乘2加2,如果此时仍小于指定的容量,那么就把新的容量设为minCapacity。然后判断是否溢出,如果溢出了,把容量设为MAX_ARRAY_SIZE(Integer.MAX_VALUE-8)

添加

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

	//String.class
	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);
    }

对于 append 方法有很多重载。对于每种重载方法的内部实现大概可以分为下面几步:

  1. 确保 append的值为有效的。如果是字符串,那么值如果为 null,则会append一个null字符串,如果是longint的值是小于最小值,那么会append一个固定值
  2. 扩容当前值。ensureCapacityInternal方法会将当前的char[] value值复制到一个新的char[] value中。
  3. 使用 getChars方法将append的值追加到扩容的value

StringBuilder & StringBuffer

StringBuilder & StringBuffer都实现了 AbstractStringBuilder接口。他们最大的不同就是StringBuilder 是线程不安全的,而StringBuffer是线程安全的。所以它们的属性和方法大致都是一样的,只是StringBuffer的部分方法上添加了Synchronized关键字来保证线程的安全。

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

StringBuilder 中没有synchronized这个关键字。对于StringBuilder & StringBuffer的大部分都是调用的父类的方法,只是最后多了一个 return this

上面的代码我们发现有这么一行代码toStringCache=null,我们来看看它的定义

	/**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient String toStringCache;

根据注释,我们可以知道这个toStringCache是用来缓冲最后一次 toString 的值,如果StringBuffer被修改了的话,那么toStringCache就会被清空。接着再来看看 toString方法

	public synchronized String toString() {
        if (toStringCache == null) {
            return toStringCache =
                    isLatin1() ? StringLatin1.newString(value, 0, count)
                               : StringUTF16.newString(value, 0, count);
        }
        return new String(toStringCache);
    }

当调用toString方法的时候,如果toStringCachenull的话,那么就会调用StringXXX.newString方法。如果不为null的话,直接返回toStringCache。而StringXXX.newString的实现如下:

    public static String newString(byte[] val, int index, int len) {
        return new String(Arrays.copyOfRange(val, index, index + len),
                          LATIN1);
    }

可以发现它其实是一个copy的操作。也就是说如果toStringCache 存在的话,就不需要再次进行copy。直接返回toStringCache,这样就可以减少不必要的消耗,提升性能。