一個關于StringBuilder與StringBuffer性能的小試驗

          StringBuilder 是從 Java 5 以后增加的一個字符串處理類。查看 API 文檔,我們可以知道 StringBuilder StringBuffer 提供同樣的功能,只是 StringBuilder 不保證線程安全,所以性能比 StirngBuffer 好,并推薦在確定線程安全的情況下,盡量用 StringBuilder 。事實真是如此嗎?讓我們通過一個小試驗來看看

          ?

          試驗設計:

          分別用 StringBuilder StringBuffer 將一指定的字符串自連接一百萬次,比較兩種方法所用的時間。為盡量避免環境的干擾,測試時會關閉本機中其它應用程序,并且為了避免測試組之間的相互干擾,在每組測試完成后會重起機器。每個程序運行十次,最后取平均值。

          ?

          測試環境:

          CPU: Celeron – M420

          RAM: 1G

          OS: Window XP Home Edition

          JDK: Sun JDK 1.6.0 (Java HotSpot? Client VM (build 1.6.0-b105, mixed mode, sharing))

          運行程序時沒有為 JVM 指定任何參數,全部使用默認值

          ?

          程序段:

          1.? StringBuffer

          ?

          ??? private static final int COUNT = 1000000;

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? public static void useStringBuffer() {

          ?????? StringBuffer bf = new StringBuffer( "" );

          ?????? String target = null ;

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? target = bf.toString();

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuffer, time is " + (end - start));

          ??? }??

          ?

          ?

          2.? StringBuilder

          ?

          ??? private static final int COUNT = 1000000;

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? public static void useStringBuilder() {

          ?????? StringBuilder bf = new StringBuilder( "" );

          ?????? String target = null ;

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? target = bf.toString();

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuilder, time is " + (end - start));

          ??? }

          ?

          ?

          測試結果:

          ?

           

          StringBuffer

          StringBuilder

          1

          328

          328

          2

          344

          312

          3

          328

          328

          4

          344

          312

          5

          344

          328

          6

          344

          312

          7

          328

          328

          8

          344

          312

          9

          343

          328

          10

          344

          328

          平均值

          339.1

          321.6

          ?

          從結果中可以看出兩者的性能差異約為 5.44

          ?

          下面我們將對測試程序做一點點小小的改動,在 new 一個新的 StringBuffer/StringBuilder 時,我們指定一個容量參數。修改的代碼如下:

          ?

          1.? StringBuffer

          ?

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? private static final int COUNT = 1000000;

          ??? public static void useStringBuffer() {

          ?????? StringBuffer bf = new StringBuffer(COUNT * TEMPLATE.length());

          ?????? String target = null ;

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? target = bf.toString();

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuffer, time is " + (end - start));

          ??? }??

          ?

          2. StringBuilder

          ?

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? private static final int COUNT = 1000000;

          ??? public static void useStringBuilder() {

          ?????? StringBuilder bf = new StringBuilder(COUNT * TEMPLATE.length());

          ?????? String target = null ;

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? target = bf.toString();

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuilder, time is " + (end - start));

          ??? }

          ?

          測試結果:(表格中第一,二組為上一輪測試的結果)

          ?

           

          StringBuffer

          StringBuilder

          StringBuffer(int)

          StringBuilder(int)

          1

          328

          328

          140

          94

          2

          344

          312

          125

          125

          3

          328

          328

          125

          93

          4

          344

          312

          125

          125

          5

          344

          328

          109

          94

          6

          344

          312

          125

          110

          7

          328

          328

          125

          110

          8

          344

          312

          110

          110

          9

          343

          328

          140

          109

          10

          344

          328

          109

          125

          平均值

          339.1

          321.6

          123.3

          109.5

          ?

          從表中可以看到 StringBuffer(int) StringBuilder(int) 兩者之間的差異為 12.6% 。但我們更應該看到采用不同的構造方法所帶來的性能提升, StringBuffer 提升了 175.02 %, StringBuilder 提升了 193.70% 。原因在于不指定 StirngBuffer/StringBuilder 的容量時,它們內部的字符緩沖區為 16 個字符(無參構造)或字符串參數的長度,當程序不斷的進行 append/insert 操作時,每當字符數超過原有的容量后, StringBuffer/StringBuilder 將不斷的進行自動擴展的工作,這將消耗比較多的時間。

          ?

          也許有人會說這樣的測試并不能反映真實的情況,因為在實際的開發中很少會在一個方法中構造 / 拼接一個長度為 10*1000000 的字符串的。更通常的情況是在一個方法中構造一個不太長的串,但該方法將被大量的,反復的調用。 OK, 我們可以修改一下測試程序來放映這種情況。

          ?

          新程序中 contactWith…. 方法用來拼接一個不太長的字符串,該方法被 use…. 方法反復調用十萬次,并記錄總的調用時間。程序如下:

          1.? 使用 StringBuffer

          ?

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? private static final int COUNT = 100000;

          ??? private static final int COUNT2 = 10;

          ??? public static String contactWithStringBuffer() {

          //???? StringBuffer bf = new StringBuffer("");

          ?????? StringBuffer bf = new StringBuffer( COUNT2 * TEMPLATE .length());

          ?????? for ( int i = 0; i < COUNT2 ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? return bf.toString();

          ??? }

          ???

          ??? public static void useStringBuffer() {

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? contactWithStringBuffer();

          ?????? }

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuffer, Time is " + (end - start));

          ??? }

          ?

          2.? 使用 StringBuilder

          ?

          ??? private static final String TEMPLATE = "0123456789" ;

          ??? private static final int COUNT = 100000;

          ??? private static final int COUNT2 = 10;

          ??? public static String contactWithStringBuilder() {

          //???? StringBuilder bf = new StringBuilder("");

          ?????? StringBuilder bf = new StringBuilder( COUNT2 * TEMPLATE .length());

          ?????? for ( int i = 0; i < COUNT2 ; i++) {

          ?????????? bf.append( TEMPLATE );

          ?????? }

          ?????? return bf.toString();

          ??? }

          ???

          ??? public static void useStringBuilder() {

          ?????? long start = System.currentTimeMillis();

          ?????? for ( int i = 0; i < COUNT ; i++) {

          ?????????? contactWithStringBuilder();

          ?????? }

          ?????? long end = System.currentTimeMillis();

          ?????? System. out .println( "Use StringBuilder, Time is " + (end - start));

          ??? }??

          ?

          測試結果:

          ?

           

          StringBuffer

          StringBuilder

          StringBuffer(int)

          StringBuilder(int)

          1

          188

          156

          140

          109

          2

          187

          172

          141

          125

          3

          188

          172

          125

          110

          4

          188

          172

          141

          110

          5

          187

          172

          125

          110

          6

          188

          172

          125

          109

          7

          172

          172

          125

          125

          8

          188

          157

          125

          110

          9

          203

          172

          125

          110

          10

          188

          172

          125

          109

          平均值

          187.7

          168.9

          129.7

          112.7

          ?

          在這種情況下, StringBuffer StringBuilder 的性能差別為: 11.13% 15.08% (使用 int 構造函數);而用不同的構造函數的性能差差異分別達到: 44.71% StringBuffer )和 49.87% StringBuilder )。并且為 StringBuffer 指定容量(使用 StirngBuffer(int) )比不指定容量的 StringBuilder 的性能高出 30.22%

          ?

          結論:

          1.? 為了獲得更好的性能,在構造 StirngBuffer StirngBuilder 時應盡可能指定它的容量。當然,如果你操作的字符串長度不超過 16 個字符就不用了。

          2.? 相同情況下使用 StirngBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。而在現實的模塊化編程中,負責某一模塊的程序員不一定能清晰地判斷該模塊是否會放入多線程的環境中運行,因此:除非你能確定你的系統的瓶頸是在 StringBuffer 上,并且確定你的模塊不會運行在多線程模式下,否則還是用 StringBuffer J

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

          ?

          特別聲明:

          1 .本人是基于 Window XP 環境,用 Sun JDK 1.6 完成的以上測試。測試的結果是否能反映其它操作系統(如 Linux, Unix 等)和不同的 JDK (IBM, Weblogic ) 的情況就不得而知,有興趣的網友可以在不同的環境中測試,歡迎您告訴我測試結果。

          2 .本人也歡迎對本測試的試驗設計和樣例代碼的合理性和完備性進行討論,但請就事論事。不要扔磚頭(西紅柿是可以的,不過不要壞的;雞蛋也可以,但不要臭的,呵呵)

          3 .今天是情人節,祝大家節日快樂,有情人終成眷屬!

          posted on 2007-02-20 21:56 Tom 閱讀(2293) 評論(5)  編輯  收藏 所屬分類: Java

          評論

          # re: 一個關于StringBuilder與StringBuffer性能的小試驗 2007-08-12 17:34 dreamstone

          這個不能這么比的,單線程下比是沒有意義的。
          只有在多線程下才能比較出兩者的區別。而且并不是多線程下一定要用stringBuffer
          多線程下并不一定要同步的。比如只讀的情況,多線程就不需要同步。這個時候測試一下stringbuffer和stringbuilder的性能吧。差距明顯的不能再明顯了。
          另外如果多線程的情況不是特別復雜還可以自己手動實現同步。  回復  更多評論   

          # re: 一個關于StringBuilder與StringBuffer性能的小試驗 2007-09-23 20:49 sitinspring

          看看.  回復  更多評論   

          # re: 一個關于StringBuilder與StringBuffer性能的小試驗 2011-10-10 17:18 路人甲

          都是牛人啊
            回復  更多評論   

          # re: 一個關于StringBuilder與StringBuffer性能的小試驗 [未登錄] 2012-02-17 22:02 abc

          而且并不是多線程下一定要用stringBuffer
          多線程下并不一定要同步的。比如只讀的情況,或不是公共資源的情況。  回復  更多評論   

          # re: 一個關于StringBuilder與StringBuffer性能的小試驗 [未登錄] 2012-04-16 08:59 icanfly

          @dreamstone
          多線程還比什么,一個是線程安全的,一個是非線程安全的,沒有可比性  回復  更多評論   

          <2012年4月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類(42)

          隨筆檔案(43)

          文章分類

          相冊

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 南安市| 即墨市| 江口县| 油尖旺区| 海门市| 聂荣县| 云梦县| 玉环县| 镇江市| 张掖市| 吉首市| 铜川市| 南郑县| 大兴区| 六盘水市| 偏关县| 西乡县| 河池市| 保定市| 鱼台县| 光山县| 枝江市| 图木舒克市| 东安县| 渝北区| 乌苏市| 本溪| 克东县| 夹江县| 宾阳县| 乌鲁木齐县| 项城市| 尼木县| 新源县| 融水| 衡南县| 丰都县| 井冈山市| 东明县| 海门市| 科技|