1. 什么是StringBuffer
大家都知道StringBuffer是可变的字符串对象,那么为什么可变呢,为什么效率比不可变字符串String要高呢,下面我们从多个角度来分析这个原因。
2. 源码继承抽象类AbstractStringBuilder
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
@Override
public int length() {
return count;
}
public int capacity() {
return value.length;
}
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 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;
}
2.1 append方法
append方法就是添加字符,返回新的截取内容,当长度不够时进行扩容。
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;
}
2.2 toString方法
toString方法就是创建一个新的String对象,赋值当前内容。
@Override
public String toString() {
return new String(value, 0, count);
}
2.3 ensureCapacityInternal扩容方法
通过源码我们可以看出,StringBuffer的底层结构和String一样都是char[]数组类型,不同的是StringBuffer有他的扩容机制,默认容量为16,当我们的内容大于我们的数组长度时,会将我们的容积扩大一倍还加2的大小。
点开我们的Arrays.copy方法
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
我们会发现它的底层也是创建了一个新的数组对象,并赋值了扩大后的长度,再调用C语言的native方法拷贝了一份,返回新的数组给我们。
3. 都是创建对象,为什么比String的效率高呢?
有同学就会问了,假如不考虑扩容机制的前提下,String和StringBuffer的效率是否是一样的呢?要回答这个问题我们只能从字节码的角度来分析。将我们的代码生成CLASS文件,再反编译成汇编码。
编译前:
public static void main(String[] args) {
String a = "a";
for (int i = 0; i< 10; i++){
a += "b";
}
System.out.println("========================");
StringBuilder c = new StringBuilder("c");
for (int i = 0; i< 10; i++){
c.append("d");
}
}
Code:
0: ldc #2 // String a
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 37
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #6 // String b
24: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #9 // String ========================
42: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: new #3 // class java/lang/StringBuilder
48: dup
49: ldc #11 // String c
51: invokespecial #12 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
54: astore_2
55: iconst_0
56: istore_3
57: iload_3
58: bipush 10
60: if_icmpge 76
63: aload_2
64: ldc #13 // String d
66: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
69: pop
70: iinc 3, 1
73: goto 57
76: return
通过汇编码,我们明显的看到,String它在拼接对象的时候是循环创建StringBuffer对象再new一个String对象,而StringBuffer则一直循环的是append方法,最后再返回一个new String对象,所以比String效率高。
|