1、StringBuilder和StringBuffer的区别
-
StringBuilder部分公开方法源码
-
StringBuffer部分公开方法源码
当使用synchronized关键字对方法进行修饰时,在多线程环境下可以保证同一时间只有一个线程可以访问和修改StringBuffer对象,从而保证了数据的一致性和线程安全,但会使其的性能变差,反之StringBuilder不使用虽然线程不安全,但其性能好。
2、数据结构(类型、定义)
-
类型
StringBuilder
和StringBuffer都继承自AbstractStringBuilder
类,AbstractStringBuilder
内部使用一个char
数组来存储字符序列,因此StringBuilder
本质上是基于char
数组实现的。
//StringBuilder继承AbstractStringBuilder类
public final class StringBuilder
extends AbstractStringBuilder
implements Serializable, CharSequence{}
//StringBuffer继承AbstractStringBuilder类
public final class StringBuffer
extends AbstractStringBuilder
implements Serializable, CharSequence{}
-
定义
在AbstractStringBuilder
类中,char
数组的定义如下:
3、初始化方式
-
无参构造方法
内部的char[ ]数组按照16的长度进行初始化
//无参构造方法内部默认为:16
public StringBuffer() {
super(16);
}
-
有参构造方法
内部的char[ ]数组按照str+length()+16的长度进行初始化
//有参构造方法内部默认为:当前传入字符串长度+16
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
4、二者的扩容机制
当使用append()
方法向二者中追加字符时,如果当前char
数组的容量不足符,就会进行扩容操作。具体的扩容步骤如下:
- 计算新的容量。新容量的计算公式为:
newCapacity = (value.length << 1) + 2
,即原容量的2倍+2。 - 检查新容量是否足够。如果新容量仍然不足以容纳新追加的字符,则将新容量设置为需要的最小容量。
- 创建一个新的
char
数组,容量为新容量。 - 将原
char
数组中的内容复制到新的char
数组中。 - 使用新的
char
数组替换原char
数组。
// AbstractStringBuilder 类中的相关方法
abstract class AbstractStringBuilder implements Appendable, CharSequence {
// 存储字符的数组
char[] value;
// 当前字符序列的长度
int count;
// 数组最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 确保容量足够的内部方法
private void ensureCapacityInternal(int minimumCapacity) {
// 如果需要的最小容量大于当前数组的长度,进行扩容
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
// 计算新容量的方法
private int newCapacity(int minCapacity) {
// 原容量的两倍加 2
int newCapacity = (value.length << 1) + 2;
// 如果新容量小于需要的最小容量,将新容量设置为需要的最小容量
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 判断新容量是否合理,如果不合理调用 hugeCapacity 方法处理
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
// 处理超大容量需求的方法
private int hugeCapacity(int minCapacity) {
// 如果需要的最小容量小于 0,说明溢出了,抛出内存不足错误
if (Integer.MAX_VALUE - minCapacity < 0) {
throw new OutOfMemoryError();
}
// 如果需要的最小容量大于最大数组容量,返回需要的最小容量,否则返回最大数组容量
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
}
// StringBuffer 类调用父类方法的示例
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
// append 方法调用 ensureCapacityInternal 确保容量
@Override
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}
5、总结
由于StringBuilder
和StringBuffer
两者都是继承AstractSbtringBuilder
父类,而且未使用final
关键字修饰char[]
数组,所以二者都是可变的。StringBuilder为使用
synchronized关键字修饰,StringBuffer使用了
synchronized关键字修饰,所以StringBuffer线程安全但性能较差,而StringBuilder虽然不安全,但性能较好。
- 扩容机制首先尝试将原容量扩大为2倍+2,如果这个新容量仍然不够,就会使用需要的最小容量作为最小容量。如果新容量足够,则使用新容量作为当前数组容量。