# 前言
StringBuilder和StringBuilder都是字符串操作类。它们都继承了AbstractStringBuilder抽象类,并且都实现了CharSequence和Serializable接口。它们和String的区别在于它们的value数组没有被final修饰符修饰,所以是可变的。它们之间的区别是StringBuffer的方法是由synchronized关键字修饰,所以是线程安全的。
# AbstractStringBuilder
AbstractStringBuilder是StringBuilder和StringBuffer共同继承的抽象类,它为子类提供了大部分的实现。
| 成员变量 & 构造方法
// 维护一个字符数组char[] value;// 字符的个数int count;// 构造方法AbstractStringBuilder() {}// 指定初始化容量AbstractStringBuilder(int capacity) {value = new char[capacity];}
| 扩容
// minimumCapacity: 需要的最小容量public void ensureCapacity(int minimumCapacity) {if (minimumCapacity > 0)ensureCapacityInternal(minimumCapacity);}private void ensureCapacityInternal(int minimumCapacity) {// 如果所需要的容量大于当前容量,则进行扩容,使用Arrays.copyOf()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) {// 新容量 = 2 * 旧容量 + 2int newCapacity = (value.length << 1) + 2;// 如果新容量仍不够大,将新容量设置为所需要容量if (newCapacity - minCapacity < 0) {newCapacity = minCapacity;}// 如果新容量 > MAX_ARRAY_SIZE or < 0return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)// 进一步判断? hugeCapacity(minCapacity)// 否则,直接返回新容量: newCapacity;}private int hugeCapacity(int minCapacity) {// 如果需要的容量大于整型最大值,抛出 OutOfMemoryErrorif (Integer.MAX_VALUE - minCapacity < 0) {throw new OutOfMemoryError();}// 所需容量 > MAX_ARRAY_SIZE,返回所需要容量// 所需容量 < MAX_ARRAY_SIZE,返回 MAX_ARRAY_SIZEreturn (minCapacity > MAX_ARRAY_SIZE)? minCapacity : MAX_ARRAY_SIZE;}
在一些虚拟机中会为数组保留一些 header words,所以为了防止OutOfMemoryError将MAX_ARRAY_SIZE设置为整型的最大值减 8。总的来说,扩容机制的过程如下:
- 新容量 = 旧容量 * 2 + 2,如果新容量仍然不能满足,数组长度直接设置为所需容量。
- 如果新容量小于
MAX_ARRAY_SIZE,直接返回新容量。 - 否则,当所需容量大于整型最大值时抛出异常。
- 如果所需容量 >
MAX_ARRAY_SIZE,直接返回所需容量,否则返回MAX_ARRAY_SIZE。| append()
append()是整个类的核心方法,拥有多个重载方法,这里主要分析以String为参数的方法。 ```java public AbstractStringBuilder append(String str) { if (str == null)
int len = str.length(); // 扩容 ensureCapacityInternal(count + len); // 调用String.getChars()将str添加到value末尾,本质是调用System.arraycopy() str.getChars(0, len, value, count); // 更新字符长度 count += len; // 返回自身,实现链式调用 return this; }return appendNull();
private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); final char[] value = this.value; // 添加’n’, ‘u’, ‘l’, ‘l’四个字符到value数组中 value[c++] = ‘n’; value[c++] = ‘u’; value[c++] = ‘l’; value[c++] = ‘l’; count = c; return this; }
<a name="YaRLd"></a># # StringBuilder<a name="BwFXm"></a>## | 构造函数```java// 默认初始化容量16public StringBuilder() {super(16);}// 指定初始化容量public StringBuilder(int capacity) {super(capacity);}// 直接传入一个Stringpublic StringBuilder(String str) {super(str.length() + 16);append(str);}// 传入一个字符序列public StringBuilder(CharSequence seq) {this(seq.length() + 16);append(seq);}
| append()
@Overridepublic StringBuilder append(String str) {super.append(str);return this;}
# StringBuffer
方法实现基本和StringBuilder相同,但是被synchronized关键字修饰,保证了方法的线程安全。
以append()为例:
// 实现和StringBuilder相同,但是添加的synchronized关键字保证了线程安全@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}
| toStringCache & toString()
StringBuffer的任何写方法都会对toStringCache重设为null,该变量保存了最近一次toString()方法的缓存。
// 最近一次toString()的缓存,任何写操作都会将该变量重设为nullprivate transient char[] toStringCache;@Overridepublic synchronized String toString() {// 如果缓存为空,则填充缓存if (toStringCache == null) {toStringCache = Arrays.copyOfRange(value, 0, count);}// 返回使用缓存new的String对象return new String(toStringCache, true);}// 该方法不是直接复制,而是将String中的value指向toStringCacheString(char[] value, boolean share) {// assert share : "unshared not supported";this.value = value;}
# 总结
- 可变性:
String是不可变的;StringBuilder和StringBuilder没有使用final和private关键字修饰底层的value数组。
- 线程安全性:
String对象是不可变的,线程安全;StringBuffer的写方法具有同步锁,线程安全;StringBuilder的写法发没有同步锁,线程不安全;
- 性能:
String类型改变时会生成一个新的String对象,并用指针指向它。StringBuffer每次对本身进行操作。- 相同情况使用
StringBuilder性能提高 10% ~ 15%,但是存在多线程不安全的风险。
- 使用:
String适合操作少量数据。StringBuilder适合单线程操作字符串缓冲区下操作大量数据。StringBuffer适合多线程操作字符串缓冲区下操作大量数据。
