java中的String不再糾結(續)


    很早之前總結過java中一些String的理解和用法,最后還體會到了其中String的一點性能上的優化。那篇博文更多的是在討論string存儲的問題,感興趣的童鞋可以看一下 傳送連接

    這兩天在淘測試的文章里看到一篇關於java string的文章,談到了StringBuilder和StringBuffer的使用效率的問題,然后發現自己忽略了capacity這個概念。比如說下面的一段代碼:

1        StringBuffer sf = new StringBuffer("");
2         sf.append("leeon");
3         System.out.println("length: "+sf.length());
4         System.out.println("capacity: "+sf.capacity());

    這個打印的結果就是

    length:5

    capacity:16

    length我們可以理解,那么capacity是什么呢?

   

     JDK源碼中給出的解釋就是用來存儲新插入的字符空間的容量,可見一個默認的容量就是16了,當然如果我們像下面這樣的去重寫上面的代碼,就會得到不一樣的結果。

1         StringBuffer sf = new StringBuffer("leeon");
2         System.out.println("length: "+sf.length());
3         System.out.println("capacity: "+sf.capacity());

     這次的結果就是

     length:5

    capacity:21

    原因就是我們在構造sf的時候本身的value已經是5了,再加上了新准備插入的初始化容量,也就是5+16.具體實現的細節是:

    

     ----------------------------------------------------------------------------------------------------------

     以上是背景知識,下面開始討論如何從capacity這個特性出發來優化性能。再仔細閱讀jdk源碼中append方法的實現的時候會發現,每次調用append都要進行容量的檢查,因為要確保StringBuffer足夠的大才能裝得下新添加的字符串。不妨再一起看下代碼:

   

 

   當調用append的時候,首先會通過ensureCapacityInternal檢查所需的容量,如果容量不足再調用expandCapacity進行擴容,可以看見擴容的方式是將現在的字符串大小增加一倍然后再加上2.如果容量仍然不足,則直接擴展到所需的大小。我們發現擴展容量就是一個耗時的操作,雖然處理大量字符串的時候采用StringBuffer會比用String更加的提升性能,但是卻仍然存在可優化的耗時部分。

  其實StringBuffer和StringBuilder在初始化的時候是可以指定capacity的,如果有一個大概的預估,初始化一個比較合適的capacity,減少擴展容量的操作,效率就會有更大的提高。比如下面的測試代碼,我們對同樣的字符串操作五百萬次,統計一下結果。

 1         StringBuilder sb = new StringBuilder(10*5000000);
 2         StringBuffer sf = new StringBuffer(10*5000000);
 3         
 4         
 5         long start = System.currentTimeMillis();  
 6         for(int i = 0; i < 5000000; i++)
 7         {
 8             sb.append("1234567890");
 9         }
10         long end = System.currentTimeMillis();
11         System.out.println("the StringBuilder run time is "+(end -start)+" ms");
12         
13         start = System.currentTimeMillis();  
14         for(int i = 0; i < 5000000; i++)
15         {
16             sf.append("1234567890");
17         }
18         end = System.currentTimeMillis();
19         System.out.println("the StringBuffer run time is "+(end -start)+" ms");

 上面的結果我沒有求平均值,測試也只是在單線程下進行的,但是數據波動不大,大致可以看出一些變化,我們發現當恰當的初始化了StringBuffer后,他的性能已經和未初始化的StringBuilder相當甚至超過了,這樣我們就能夠在保持和原來StringBuilder相當效率的基礎上有更好的安全性了。

  可以看到采用了初始化的容量,我們獲得的性能提升要高於從SF切換到SB。

  淘測試總結的結論中有一個我很喜歡,引用一下

  “ 用好現有的類比引入新的類更重要。很多程序員在使用 StringBuffer 時是不指定其容量的(至少我見到的情況是這樣),如果這樣的習慣帶入 StringBuilder 的使用中,你將只能獲得 10 %左右的性能提升(不要忘了,你可要冒多線程的風險噢);但如果你使用指定容量的 StringBuffer ,你將馬上獲得 45% 左右的性能提升,甚至比不使用指定容量的 StirngBuilder 都快 30% 左右。”

  這個問題其實后續還值得討論,現在我們看到都是單線程的情況,那么多線程情況下,StringBuffer是不是可以優化的更多了?

  忽然發現,自己忽略了很多java的特性,學在當下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM