String a=String b + String c + String d
這種代碼在程序里應該隨處可見,一部分人不知道這段代碼的缺陷在哪,另一部分人知道這樣寫不好,但是太順手了下意識就寫了。
在Java里,String是個不可變對象,所以右邊的每次賦值操作都會new一個新對象,b+c,b+c+d,至少會new兩個,很明顯性能不佳。但是這個問題沒有大多數情況下沒我們想象的那么嚴重,因為編譯器在編譯時會對String做很多優化,但是對於一些運行時的賦值和修改操作,編譯器很難優化,這種時候,就強烈不推薦這樣寫,雖然寫的很順手 ,但是性能不行,如果存在字符串的修改操作,就應該用StringBuilder和StringBuffer。
下面,就用一個簡單的測試,來看看他們之間的性能差別,讓我們心里有數。
1. 測試String直接拼接
1 public static void main(String[] args) { 2 long begin = System.currentTimeMillis(); 3 String str = ""; 4 for(int i=0;i<10000;i++){ 5 str = str+i; 6 } 7 long end = System.currentTimeMillis(); 8 long time = end - begin; 9 System.out.println(time+""); 10 }
直接用String的拼接,循環調用10000次,跑出來結果是639毫秒
2.測試String.concat拼接
1 public static void main(String[] args) { 2 long begin = System.currentTimeMillis(); 3 String str = ""; 4 for(int i=0;i<10000;i++){ 5 str = str.concat(String.valueOf(i)); 6 } 7 long end = System.currentTimeMillis(); 8 long time = end - begin; 9 System.out.println(time+""); 10 }
也是循環調用10000次,跑出來結果是322毫秒
3.測試StringBuilder拼接
1 public static void main(String[] args) { 2 long begin = System.currentTimeMillis(); 3 StringBuilder sb = new StringBuilder(); 4 for(int i=0;i<10000;i++){ 5 sb.append(i); 6 } 7 long end = System.currentTimeMillis(); 8 long time = end - begin; 9 System.out.println(time+""); 10 } 11 12 }
同樣循環調用10000次,結果是5毫秒。換成StringBuffer,結果也是一樣。
上面這個簡單測試,可以用數據告訴我們,String和StringBuilder、StringBuffer之間的性能差距了,所以如果字符串有變更,都用StringBuilder和StringBuffer,而不是用簡單粗暴的賦值操作,另外就是,上面可以看出,String的concat性能也明顯優於直接拼接。
關於StringBuilder和StringBuffer
這兩個操作可變字符串的類,都實現了AbstractStringBuilder抽象類,接口也幾乎一樣,他的最大區別是:StringBuffer基本所有方法都做了同步,而StringBuilder沒有,換言之,StringBuffer是線程安全的,StringBuilder不是。所以這兩個就根據不同的場景做取舍就ok。
謝謝@assiwe提醒,由於編譯器本身對String做了足夠多的優化,對於簡單的String操作,String不比StringBuilder差,因為本身開銷都很少,這種時候苛求性能就沒太多意義了,只有在我們對String操作的性能敏感的時候,才需要關注他們之間的性能差異,用的時候,要做到心里有數,明白為什么用這個而不用另一個。
