前言
本文针对于String类型相似的StringBuffer和 StringBuilder类的区别及StringBuffer和StringBuilder 类扩容。下面开始介绍。 先说一下String、StringBuffer、StringBuilder三者的异同如下:
| String | StringBuffer | StringBuilder |
|---|---|---|
| 不可变 | 可变 | 可变 |
| 线程安全 | 线程不安全 | |
| 多线程操作字符串 | 单线程操作字符串 |
String、StringBuffer 和 StringBuilder 三者的继承结构如下:
String
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间。
特点
- String类是被final修饰的,是不能被继承的。
- String类底层使用数组结构。jdk9以前使用的是char[],jdk9以后使用的是byte[]。
- String的对象一旦创建就不能修改,底层维护了一个字符串常量池,实现共享。
StringBuffer
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
特点
- 可变长
- 线程安全的
- 多线程操作
- 效率低
StringBuilder
StringBuilder是可变类,和线程不安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuilder对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
特点
- 可变长
- 线程不安全的
- 单线程操作
- 效率高
StringBuffer和StringBuilder的初始容量及扩容
StringBuffer和StringBuilder初始的空闲容量都是16,当调用append方法时调用的都是super.append即AbstractStringBuilder中方法,所以扩容机制相同。
StringBuffer类有StringBuffer(),StringBuffer(int capacity),StringBuffer(String str)三个改造方法。
- StringBuffer()的初始容量可以容纳16个字符,当该对象的实体存放的字符的长度大于16时,实体容量就自动增加。StringBuffer对象可以通过length()方法获取实体中存放的字符序列长度,通过capacity()方法来获取当前实体的实际容量。
public StringBuilder() {
super(16);
}
- StringBuffer(int capacity)可以指定分配给该对象的实体的初始容量参数为参数size指定的字符个数。当该对象的实体存放的字符序列的长度大于size个字符时,实体的容量就自动的增加。以便存放所增加的字符。
public StringBuilder(int capacity) {
super(capacity);
}
- StringBuffer(String str)可以指定给对象的实体的初始容量为参数字符串s的长度额外再加16个字符。当该对象的实体存放的字符序列长度大于size个字符时,实体的容量自动的增加,以便存放所增加的字符。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
接下来介绍StringBuffer怎样扩容: StringBuffer通过append方法中的ensureCapacityInternal方法可以看出当追加后超出容量会触发扩容,通过newCapacity获得新容量。
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 void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
计划扩容为++2*n+2++,n为扩容前容量,如果追加后长度超出则扩容为++n+count++,count为追加长度。由此可见下次append还会触发扩容机制。所以在设计时应该避免,最好能在初始时设置一个合理的容量。
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;
}
总结
- 如果要操作少量的数据用 String;
- 多线程操作字符串缓冲区下操作大量数据 StringBuffer
- 单线程操作字符串缓冲区下操作大量数据 StringBuilder;
- StringBuffer和StringBuilder的区别就在于StringBuffer的操作使用synchronized关键字加了锁,是线程安全的。