jdk版本爲1.8
StringBuffer與StringBuilder繼承自同一父類AbstractStringBuilder,append的源碼分別爲
StringBuffer
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuilder
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
其中toStringCache 爲用在toString方法中的緩存變量,與字符串拼接無關,所以,這兩個類在拼接字符串時的唯一區別就只有同步。
同步上的區別具體體現在哪
運行下面這段代碼(具體循環次數需要根據電腦配置來定,否則有可能無法輸出預期結果)
import java.util.concurrent.CountDownLatch;
public class Test02 {
static StringBuilder str = new StringBuilder();
static String append1 = "1111111111";
static String append2 = "aaaaaaaaaa";
public static void main(String[] args) throws InterruptedException {
CountDownLatch cl = new CountDownLatch(2);
new Thread(() -> {
for(int i = 0; i < 1000; i++) {
str.append(append1 + "\r\n");
}
cl.countDown();
System.out.println(str);
}).start();
new Thread(() -> {
for(int i = 0; i < 1000; i++) {
str.append(append2 + "\r\n");
}
cl.countDown();
System.out.println(str);
}).start();
cl.await();
}
}
會在控制檯看到類似這樣的輸出
這就是非同步代碼在多線程下產生的錯誤,而在線程安全的StringBuffer中則不會出現這種問題。
具體到源碼上來分析的話,這兩個類在底層調用的都是
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
原因就是在複製時出現了不同步的問題。
StringBuffer,StringBuilder與String在拼接字符串時效率的差異
既然都看源碼了,就順便說一下
首先是String的加號(+),這個是毫無爭議的最慢,具體原因需要反編譯來分析,有時間再搞。這裏主要比較String的concat與AbstractStringBuilder的append
運行下面代碼
public class Test01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = "";
s1.concat("1");
StringBuffer s2 = new StringBuffer("");
s2.append("1");
StringBuilder s3 = new StringBuilder("");
s3.append("1");
long time = System.currentTimeMillis();
for(int i = 0; i < 200000; i++) {
s1 = s1.concat("1");
}
System.out.println(System.currentTimeMillis() - time);
time = System.currentTimeMillis();
s1 = "";
for(int i = 0; i < 200000; i++) {
s2.append("1");
}
System.out.println(System.currentTimeMillis() - time);
time = System.currentTimeMillis();
for(int i = 0; i < 200000; i++) {
s3.append("1");
}
System.out.println(System.currentTimeMillis() - time);
time = System.currentTimeMillis();
}
}
輸出
6602
6
2
可以看出StringBuilder由於沒有同步,所以略微快於StringBuffer,但是concat的速度則是遠遠慢於後面兩個,這可以從源碼上輕鬆分析出來
concat
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
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;
}
可以看到,concat是每進行一次拼接,都會字符串數組進行一次複製,而append方法則有一個類似緩存的機制。ensureCapacityInternal方法會在數組長度不足時進行倍增複製,當長度足夠時,append方法只是將新加入的字符串直接複製到數組的末尾。總的來說,就是concat方法進行數組複製的次數要遠大於append,自然速度就慢了。