隨筆-8  評(píng)論-39  文章-0  trackbacks-0

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

          ?

          試驗(yàn)設(shè)計(jì):

          分別用 StringBuilder StringBuffer 將一指定的字符串自連接一百萬(wàn)次,比較兩種方法所用的時(shí)間。為盡量避免環(huán)境的干擾,測(cè)試時(shí)會(huì)關(guān)閉本機(jī)中其它應(yīng)用程序,并且為了避免測(cè)試組之間的相互干擾,在每組測(cè)試完成后會(huì)重起機(jī)器。每個(gè)程序運(yùn)行十次,最后取平均值。

          ?

          測(cè)試環(huán)境:

          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))

          運(yùn)行程序時(shí)沒(méi)有為 JVM 指定任何參數(shù),全部使用默認(rèn)值

          ?

          程序段:

          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));

          ??? }

          ?

          ?

          測(cè)試結(jié)果:

          ?

           

          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

          ?

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

          ?

          下面我們將對(duì)測(cè)試程序做一點(diǎn)點(diǎn)小小的改動(dòng),在 new 一個(gè)新的 StringBuffer/StringBuilder 時(shí),我們指定一個(gè)容量參數(shù)。修改的代碼如下:

          ?

          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));

          ??? }

          ?

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

          ?

           

          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% 。但我們更應(yīng)該看到采用不同的構(gòu)造方法所帶來(lái)的性能提升, StringBuffer 提升了 175.02 %, StringBuilder 提升了 193.70% 。原因在于不指定 StirngBuffer/StringBuilder 的容量時(shí),它們內(nèi)部的字符緩沖區(qū)為 16 個(gè)字符(無(wú)參構(gòu)造)或字符串參數(shù)的長(zhǎng)度,當(dāng)程序不斷的進(jìn)行 append/insert 操作時(shí),每當(dāng)字符數(shù)超過(guò)原有的容量后, StringBuffer/StringBuilder 將不斷的進(jìn)行自動(dòng)擴(kuò)展的工作,這將消耗比較多的時(shí)間。

          ?

          也許有人會(huì)說(shuō)這樣的測(cè)試并不能反映真實(shí)的情況,因?yàn)樵趯?shí)際的開(kāi)發(fā)中很少會(huì)在一個(gè)方法中構(gòu)造 / 拼接一個(gè)長(zhǎng)度為 10*1000000 的字符串的。更通常的情況是在一個(gè)方法中構(gòu)造一個(gè)不太長(zhǎng)的串,但該方法將被大量的,反復(fù)的調(diào)用。 OK, 我們可以修改一下測(cè)試程序來(lái)放映這種情況。

          ?

          新程序中 contactWith…. 方法用來(lái)拼接一個(gè)不太長(zhǎng)的字符串,該方法被 use…. 方法反復(fù)調(diào)用十萬(wàn)次,并記錄總的調(diào)用時(shí)間。程序如下:

          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));

          ??? }??

          ?

          測(cè)試結(jié)果:

          ?

           

          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 構(gòu)造函數(shù));而用不同的構(gòu)造函數(shù)的性能差差異分別達(dá)到: 44.71% StringBuffer )和 49.87% StringBuilder )。并且為 StringBuffer 指定容量(使用 StirngBuffer(int) )比不指定容量的 StringBuilder 的性能高出 30.22%

          ?

          結(jié)論:

          1.? 為了獲得更好的性能,在構(gòu)造 StirngBuffer StirngBuilder 時(shí)應(yīng)盡可能指定它的容量。當(dāng)然,如果你操作的字符串長(zhǎng)度不超過(guò) 16 個(gè)字符就不用了。

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

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

          ?

          特別聲明:

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

          2 .本人也歡迎對(duì)本測(cè)試的試驗(yàn)設(shè)計(jì)和樣例代碼的合理性和完備性進(jìn)行討論,但請(qǐng)就事論事。不要扔磚頭(西紅柿是可以的,不過(guò)不要壞的;雞蛋也可以,但不要臭的,呵呵)

          3 .今天是情人節(jié),祝大家節(jié)日快樂(lè),有情人終成眷屬!

          posted on 2007-02-14 08:00 Jini 閱讀(1898) 評(píng)論(14)  編輯  收藏 所屬分類: JDK相關(guān)

          評(píng)論:
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-14 10:42 | alysa
          Please help to answer the question as below,
          What method should class extending String override?
            回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn)[未登錄](méi) 2007-02-14 11:08 | jini
          @alysa

          The String is final class, no class can be extend it.

          hehe... I am not sure I answer your question. If no, please give me more detail about your question.  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-14 13:36 | cuiyi
          頂一個(gè)!希望以后能再次看到你的好貼!  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-14 13:51 | Jini
          @cuiyi

          謝謝你的鼓勵(lì) :)

          其實(shí)我只是把日常工作和學(xué)習(xí)的心得體會(huì)紀(jì)錄下來(lái)與大家分享,希望大家能共同學(xué)習(xí),共同進(jìn)步,呵呵。
            回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-15 08:40 | 不好說(shuō)
          StringBuffer和StringBuilder在toString這個(gè)方法上處理不一樣。

          StringBuffer主要用于在多線程并發(fā)存貯同一個(gè)實(shí)例上才有優(yōu)勢(shì)以。
          web編程很少出現(xiàn)這種情況。
            回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn)[未登錄](méi) 2007-02-15 10:21 | jini
          謝謝你的回復(fù)!

          如果是局部變量,應(yīng)該不存在線程安全的問(wèn)題。但可能會(huì)面臨重復(fù)構(gòu)造對(duì)象的問(wèn)題,就像每次調(diào)用contactWith....方法都會(huì)構(gòu)造一個(gè)新的StringBuffer或StringBuilder,為避免構(gòu)造大量的對(duì)象,可能會(huì)考慮一些簡(jiǎn)單的重用對(duì)象的方法,這時(shí)就有可能引入多線程的問(wèn)題了。  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-15 19:18 | BeanSoft
          博主這種認(rèn)真的態(tài)度偶太敬佩了!!! 現(xiàn)在的大牛們總是隨口就是優(yōu)化, 性能, 集群, Cache ... 唉, 不知道他們自己試過(guò)了沒(méi).  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-15 20:23 | 喜來(lái)了
          其實(shí)StringBuilder就是刪除StringBuffer的synchronized定義的一個(gè)新類, 在單一線程的情況,兩者的差別取決synchronized和非synchronized函數(shù)的差別. 由于Java的線程是native的, 應(yīng)該和操作系統(tǒng)很有關(guān)系。按摟主(初來(lái)乍到,不知道怎么稱呼,叫錯(cuò)了勿怪)的測(cè)試,在Windows XP下相差不大,不知道在其他系統(tǒng)下怎么樣。  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-15 21:02 | Jini
          @BeanSoft

          謝謝你的回復(fù)! 以后有機(jī)會(huì)多交流 - Good Good Study, Day Day Up :)

            回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-02-15 21:09 | Jini
          @喜來(lái)了

          謝謝你的回復(fù)! 你分析的有道理,不過(guò)我沒(méi)有在其它環(huán)境測(cè)試過(guò),有時(shí)間補(bǔ)上,說(shuō)不定又有些不同的認(rèn)識(shí)了,呵呵.....  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn)[未登錄](méi) 2007-02-16 21:22 | nake
          認(rèn)真仔細(xì)的程序員重視能令人佩服和值得尊重.  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-03-17 11:00 | kun
          非常嚴(yán)謹(jǐn)直觀的對(duì)比。
          感謝分享。  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2007-03-17 13:12 | kun
          請(qǐng)教:
          1.如果測(cè)試時(shí)StringBuilder和String執(zhí)行的不是附加,而是循環(huán)賦新值操作呢?
          (StringBuilder必須通過(guò)replace或者先delete再附加來(lái)實(shí)現(xiàn)重新賦值?)
          2.聲明一個(gè)String和聲明一個(gè)StringBuilder再toString哪個(gè)開(kāi)銷比較大?  回復(fù)  更多評(píng)論
            
          # re: 一個(gè)關(guān)于StringBuilder與StringBuffer性能的小試驗(yàn) 2011-11-28 21:27 | xinyonda
          主站蜘蛛池模板: 巴彦淖尔市| 东至县| 宾阳县| 新龙县| 陆丰市| 朝阳市| 开化县| 聂荣县| 鹤庆县| 曲周县| 邯郸县| 林口县| 北安市| 当涂县| 德惠市| 徐州市| 凤城市| 油尖旺区| 临西县| 正安县| 井冈山市| 永善县| 黄山市| 旺苍县| 满洲里市| 遂宁市| 伊川县| 边坝县| 杂多县| 江口县| 东明县| 江都市| 斗六市| 济南市| 高陵县| 平度市| 沅陵县| 定结县| 海林市| 无为县| 恩平市|