StringBuffer與StringBuilder拼接字符串時的區別

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,自然速度就慢了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章