前言
StringBuilder
和StringBuilder
都是字符串操作类。它们都继承了AbstractStringBuilder
抽象类,并且都实现了CharSequence
和Serializable
接口。它们和String
的区别在于它们的value
数组没有被final
修饰符修饰,所以是可变的。它们之间的区别是StringBuffer
的方法是由synchronized
关键字修饰,所以是线程安全的。
AbstractStringBuilder
AbstractStringBuilder
是StringBuilder
和StringBuffer
共同继承的抽象类,它为子类提供了大部分的实现。
成员变量 & 构造方法
1 2 3 4 5 6 7 8 9 10 11 12
| char[] value;
int count;
AbstractStringBuilder() { }
AbstractStringBuilder(int capacity) { value = new char[capacity]; }
|
扩容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); }
private void ensureCapacityInternal(int minimumCapacity) { 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) { 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) { throw new OutOfMemoryError(); } return (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
为参数的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public StringBuilder() { super(16); }
public StringBuilder(int capacity) { super(capacity); }
public StringBuilder(String str) { super(str.length() + 16); append(str); }
public StringBuilder(CharSequence seq) { this(seq.length() + 16); append(seq); }
|
append()
1 2 3 4 5
| @Override public StringBuilder append(String str) { super.append(str); return this; }
|
StringBuffer
方法实现基本和StringBuilder
相同,但是被synchronized
关键字修饰,保证了方法的线程安全。
以append()
为例:
1 2 3 4 5 6 7
| @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
|
toStringCache & toString()
StringBuffer
的任何写方法都会对toStringCache
重设为null
,该变量保存了最近一次toString()
方法的缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private transient char[] toStringCache;
@Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
String(char[] value, boolean share) { this.value = value; }
|
总结
- 可变性:
String
是不可变的;
StringBuilder
和StringBuilder
没有使用final
和private
关键字修饰底层的value数组。
- 线程安全性:
String
对象是不可变的,线程安全;
StringBuffer
的写方法具有同步锁,线程安全;
StringBuilder
的写法发没有同步锁,线程不安全;
- 性能:
String
类型改变时会生成一个新的String
对象,并用指针指向它。
StringBuffer
每次对本身进行操作。
- 相同情况使用
StringBuilder
性能提高 10% ~ 15%,但是存在多线程不安全的风险。
- 使用:
String
适合操作少量数据。
StringBuilder
适合单线程操作字符串缓冲区下操作大量数据。
StringBuffer
适合多线程操作字符串缓冲区下操作大量数据。