查看更多文章 我的博客網站 www.caoyong.xin
Java中的字符串有兩類 一種是String,一種是StringBuffer/StringBuilder,一般知道對於字符串拼接,String類使用的是+,而StringBuffe/StringBuilder使用的是append方法,使用後者的效率要比前者高,我們還知道StringBuffer是線程安全的,StringBuilder是線程不安全的,所以StringBuffer效率要比StringBuilder低。針對上面兩個問題我們只知道表面,卻不知道其中的門門道道。所以接下來就從源碼的角度分析上面兩種情況產生的原因所在。
目錄
1:爲什麼StringBuffer/StringBuilder要比String的效率高?
2:爲什麼StringBuffer是線程安全,而StringBuilder不是線程安全?
1:爲什麼StringBuffer/StringBuilder要比String的效率高?
關於這個問題首先我們用代碼來直觀體驗一下?
代碼如下:
public class Test {
public static void testString() {
System.out.println("==========================================");
String a = new String();
long start = System.currentTimeMillis();
for(int i = 0 ;i<100000 ; i++) {
a += "a";
}
System.out.println("String所花時間"+String.valueOf(System.currentTimeMillis()-start));
}
public static void testStringbuffer() {
System.out.println("==========================================");
StringBuffer a = new StringBuffer();
long start = System.currentTimeMillis();
for(int i = 0 ;i<100000 ; i++) {
a.append("a");
}
System.out.println("Stringbuffer所花時間"+String.valueOf(System.currentTimeMillis()-start));
}
public static void testStringBuilder() {
System.out.println("==========================================");
StringBuilder a = new StringBuilder();
long start = System.currentTimeMillis();
for(int i = 0 ;i<100000 ; i++) {
a.append("a");
}
System.out.println("Stringbuilder所花時間"+String.valueOf(System.currentTimeMillis()-start));
}
public static void main(String[] args) {
Test.testString();
Test.testStringbuffer();
Test.testStringBuilder();
}
}
運行上述代碼:
==========================================
String所花時間4984
==========================================
Stringbuffer所花時間15
==========================================
Stringbuilder所花時間15
可以看到在循環次數較大的情況下,StringBuffer/StringBuilder要比String所花時間少的多。而且再把循環次數調到大點,有可能就會報內存溢出異常。
對於String來說有一句話比較經典“String是不變的”,怎麼個不變法?
首先對於上面的問題先放一放,來說一下String。
在Java中String定義爲final類型,說明不能被繼承,簡單點說,對於如此高頻率被使用的數據類型,設計者們認爲已經設計的足夠優秀了,不需要被繼承,否則胡亂繼承重寫可能會降低程序的性能。其次對於String類型來說不管是拼接使用的+還是String自帶的方法如replace,substring等方法。返回的都是一個新的String類型對象引用,從在內存中重新開闢一個空間存放新的String類型對象,以及一個新的對象引用。下面來看一個例子
public class Test1 {
public static void change(String str) {
//執行replace方法後的str是一個新的引用
str = str.replace("A", "a");
//執行substring方法後又是一個新的引用
str = str.substring(1);
}
public static void main(String[] args) {
String str = "AABBCC";//創建一個str對象
change(str);//調用change方法
System.out.println(str);//注意這裏調用的是String str = "AABBCC"的str引用
}
}
輸出結果是AABBCC 而不是aBBCC.。這也就印證了,對於String類型來說不管是拼接使用的+還是String自帶的方法如replace,substring等方法。返回的都是一個新的String類型對象引用,從在內存中重新開闢一個空間存放新的String類型對象,以及一個新的對象引用。
所以對於String來說每次的拼接字符串都要在內存中開闢新空間,存放拼接後的字符串,創建新引用。這一部分在頻繁操作後,會消耗大量時間。
而對於StringBuffer/StringBuilder來說他們是怎樣拼接字符串的了?
查看StringBuffer/StringBuilder的源碼,(StringBuffer/StringBuilder源碼大致一樣)
這是append方法。我們繼續點擊super.append(str),然後繼續點擊,直到來到最底層
可以看到這裏是用來System.arraycopy()方法。該方法是複製一個數組(ariginal)中的一部分放到(copy)數組中的指定位置(關於該方法可以查看API)。所以這裏就可以看到對於StringBuffer/StringBuilder的append方法,他其實是複製需要拼接的字符串,放到原來字符串的後面。而不是產生一個新的字符串。所以花費的時間就比較短,效率也就很高了。
2:爲什麼StringBuffer是線程安全,而StringBuilder不是線程安全?
還是看源碼。我們看StringBuffer的源碼,對於StringBuffer類中所有的方法都使用了synchronized關鍵字。而synchronized關鍵字就是同步的意思。也就保證的在使用Stringbuffer的時候所有的線程都是同步的,即線程安全的
而對於StringBuilder確沒有synchronized關鍵字,所以它不是線程安全的。對於StringBuffer來說他是線程安全的,那麼既然要保證線程安全,那麼就會付出一些代價,花費的時間就會比StringBuilder要多一下。我們把最開始的例子循環次數在加大一些。(testString()就不要加大了不然會崩潰)
public static void testStringbuffer() {
System.out.println("==========================================");
StringBuffer a = new StringBuffer();
long start = System.currentTimeMillis();
for(int i = 0 ;i<10000000 ; i++) {
a.append("a");
}
System.out.println("Stringbuffer所花時間"+String.valueOf(System.currentTimeMillis()-start));
}
public static void testStringBuilder() {
System.out.println("==========================================");
StringBuilder a = new StringBuilder();
long start = System.currentTimeMillis();
for(int i = 0 ;i<10000000 ; i++) {
a.append("a");
}
System.out.println("Stringbuilder所花時間"+String.valueOf(System.currentTimeMillis()-start));
}
輸出結果:
==========================================
Stringbuffer所花時間473
==========================================
Stringbuilder所花時間142
這樣就看出對比來了。
好了兩個問題都解決了。