系列文章:
- JDK源码分析-transient关键字
- JDK源码分析-transient关键字
- JDK源码分析-String
- JDK源码分析-AbstractStringBuilder
- JDK源码分析-StringBuffer和StringBuilder
- JDK源码分析-RandomAccess
- JDK源码分析-ArrayList
- JDK源码分析-LinkedList
- JDK源码分析-HashMap
- JDK源码分析-HashSet
- JDK源码分析-LinkedHashMap
- JDK源码分析-ConcurrentHashMap
一.总览
StringBuffer 是在JDK1.0就引入了,因为String的不可变性(为什么Java中String是不可变的),字符串的拼接和裁剪等功能就会产生新的String对象,所以对性能有很大影响。 StringBuffer 是为解决上面提到拼接产生太多中间对象的问题而提供的一个类,我们可以用 append 或者 add 方法,把字符串添加到已有序列的末尾或者指定位置。StringBuffer 本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销。 StringBuilder是在JDK1.5引入的,在功能上和StringBuffer没有区别,但是去掉了线程安全部分,也就有效的减小了性能开销。
二.相同点
2.1 StringBuffer和StringBuilder有相同的继承结构
他们都继承了AbstractStringBuilder类,都实现了Serializable和CharSequence接口
2.2 StringBuffer和StringBuilder都是可变对象
他们底层都是利用可修改的char[]数据来存储对象(JDK1.9中是byte数组),且数组的最大长度MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8。 默认初始长度为16,每次append()的时候,都会校验当前数组长度是否足够,如果不够就会扩容。扩容会产生多重开销,因为要抛弃原有数组,创建新的数组(从源码newCapacity中看出,数组扩容是2*n+2),然后进行 arraycopy
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;
}
三.不同点
StringBuffer的所有方法都加上了synchronized方法,因此是线程安全的;StringBuilder的所有方法和StringBuffer相同,只是少了synchronized修饰,因此不是线程安全的,但是性能更好,所以在不要求线程安全的条件下,推荐使用StringBuilder
3.1 StringBuffer和StringBuilder性能测试
在较小数据量的情况下,二者差别不大
@State(Scope.Benchmark)
public static class MyState {
int iterations = 1000;
String initial = "abc";
String suffix = "def";
}
@Benchmark
public StringBuffer benchmarkStringBuffer(MyState state) {
StringBuffer stringBuffer = new StringBuffer(state.initial);
for (int i = 0; i < state.iterations; i++) {
stringBuffer.append(state.suffix);
}
return stringBuffer;
}
@Benchmark
public StringBuilder benchmarkStringBuilder(MyState state) {
StringBuilder stringBuilder = new StringBuilder(state.initial);
for (int i = 0; i < state.iterations; i++) {
stringBuilder.append(state.suffix);
}
return stringBuilder;
}
使用默认的Throughput模式,单位时间次数如下:
Benchmark Mode Cnt Score Error Units
StringBufferStringBuilder.benchmarkStringBuffer thrpt 200 86169.834 ± 972.477 ops/s
StringBufferStringBuilder.benchmarkStringBuilder thrpt 200 91076.952 ± 2818.028 ops/s
当把循环次数从1000增加到10000结果如下:
Benchmark Mode Cnt Score Error Units
StringBufferStringBuilder.benchmarkStringBuffer thrpt 200 77.178 ± 0.898 ops/s
StringBufferStringBuilder.benchmarkStringBuilder thrpt 200 85.769 ± 1.966 ops/s
四.总结
StringBuffer是线程安全的,但是效率低 StringBuilder非线程安全,效率稍高