<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Oo緣來(lái)是你oO


    posts - 120,comments - 125,trackbacks - 0

    如何讓你的程序運(yùn)行的更快(1)---StringVSStringBuffer

    馬嘉楠 2006-09-14

    最開始給這篇文章起名的時(shí)候準(zhǔn)備叫“如何讓你的程序運(yùn)行的更快(1)之String StringBuffer誰(shuí)與爭(zhēng)鋒!”,后來(lái)想想武俠的味道太濃了,有吸引眼球騙取點(diǎn)擊率的嫌疑,還是簡(jiǎn)單一點(diǎn)的好,畢竟這是技術(shù)不是文學(xué),需要嚴(yán)謹(jǐn)。你說(shuō)呢?^+^

    在總體的框架設(shè)計(jì)確定以后,多注意一些編程細(xì)節(jié),積少成多,可以獲得更佳的性能,讓程序跑的更快!

    前些天同事優(yōu)化代碼時(shí)提到了String和StringBuffer。仔細(xì)想想,發(fā)現(xiàn)自己也就是知道個(gè)大概,所以查了 一下資料。文章中有錯(cuò)誤的地方你可一定要告訴我啊,呵呵^+^

    一.介紹

    String:非可變類(immutable),一旦創(chuàng)建就不能再被改變。
    StringBuffer:可變類,創(chuàng)建之后可以被改變。

    何謂非可變類?
    簡(jiǎn)單說(shuō),非可變類的實(shí)例是不能被修改的,每個(gè)實(shí)例中包含的信息都必須在該實(shí)例創(chuàng)建的時(shí)候就提供出來(lái) ,并且在對(duì)象的整個(gè)生存周期內(nèi)固定不變。

    非可變類好處:狀態(tài)單一,對(duì)象簡(jiǎn)單,便于維護(hù);通常是線程安全的;用戶可以共享非可變對(duì)象,甚至可 以共享它們的內(nèi)部信息


    二.創(chuàng)建字符串

    兩種方法:
    1.?String s1 = "hello";
    2.?String s2 = new String("hello");

    哪種方式性能更好?
    例1:

    ?1?//create?String?without?"new"?keyword
    ?2?long?startTime1?= ?System.currentTimeMillis();
    ?3?for(int?i=0;i<100000;i++
    ){
    ?4?????? ?String?str1? = ? " hello "
    ;
    ?5?
    }
    ?6?long?endTime1?=
    ?System.currentTimeMillis();
    ?7?System.out.println("create?String?without?'new'?keyword?:?"?+?(endTime1?-?startTime1)?+?"?milli?seconds"
    ?);
    ?8?
    ?????
    ?9?//create?String?with?'new'?keyword???????

    10?long?startTime2?= ?System.currentTimeMillis();
    11?for(int?i=0;i<100000;i++
    ){
    12???????String?str2?=?new?String("hello"
    );
    13?
    }
    14?long?endTime2?=
    ?System.currentTimeMillis();
    15?System.out.println("create?String?with?'new'?keyword?:?"?+?(endTime2?-?startTime2)?+?"?milli?seconds");


    輸出結(jié)果為(注:程序的輸出結(jié)果也許和你的結(jié)果不同,但是總體趨勢(shì)應(yīng)該是一致的):

    create?String?without? ' new ' ?keyword?:? 0 ?milli?seconds
    create?String?with?
    ' new ' ?keyword?:? 16 ?milli?seconds

    結(jié)論: 創(chuàng)建字符串變量時(shí)盡可能不使用new關(guān)鍵字
    ?
    說(shuō)明:
    雖然兩個(gè)語(yǔ)句都是返回一個(gè)String對(duì)象的引用,但是jvm對(duì)兩者的處理方式是不一樣的。

    對(duì)于第一種不用new關(guān)鍵字創(chuàng)建String對(duì)象:
    JVM首先會(huì)在內(nèi)部維護(hù)的滯留字符串中通過(guò)String的equels方法查找是對(duì)象池中是否存放有該 String對(duì)象。如果有,返回已有的String對(duì)象給用戶,而不會(huì)在heap中重新創(chuàng)建一個(gè)新的String對(duì)象;如 果對(duì)象池中沒(méi)有該String對(duì)象,JVM則在heap中創(chuàng)建新的String對(duì)象,將其引用返回給用戶,同時(shí)將該引 用添加至滯留字符串中。

    對(duì)于第二種使用new關(guān)鍵字創(chuàng)建String對(duì)象:
    JVM會(huì)馬上在heap中創(chuàng)建一個(gè)String對(duì)象,然后將該對(duì)象的引用返回給用戶。JVM是不會(huì)主動(dòng)把該 對(duì)象放到滯留字符串里面的,除非程序調(diào)用 String的intern()方法.


    JVM為字符串自變量維護(hù)一些唯一的String對(duì)象,程序員不需要為字符串自變量而發(fā)愁。但是使用new關(guān)鍵 字可能會(huì)在內(nèi)存中創(chuàng)建重復(fù)的String對(duì)象,你不必為此而煩惱,intern()方法可以幫你解決問(wèn)題。

    String.intern():檢查字符串對(duì)象的存在性,如果需要的字符串對(duì)象已經(jīng)存在,那么它會(huì)將引用指向已 經(jīng)存在的字符串對(duì)象而不是重新創(chuàng)建一個(gè)。

    例2:

    ?1?String?str3?=?"world";??????/*JVM在滯留字符串中找不到值為“world”的字符串,就在堆上創(chuàng)建一個(gè)string對(duì)象,并將該對(duì)象的引用加入到滯留字符串中*/
    ?2?
    ?3?String?str4?=?new?String("world");??????/*JVM在堆上重新創(chuàng)建一個(gè)值為“world”的字符串,此時(shí)堆上有兩個(gè)值為“world”的字符串*/
    ?4?if(str3?== ?str4){
    ?5???????System.out.println("str3?==?str4"
    );
    ?6?
    }
    ?7?else
    {
    ?8???????System.out.println("str3?!=?str4"
    );
    ?9?
    }
    10?//輸出:str3?!=?str4

    11?
    12?String?str5?=?"world";??????/*JVM在發(fā)現(xiàn)滯留字符串中存在“world”對(duì)象,因此返回str3指向的對(duì)象給str5,即str3和str5是指向同一個(gè)對(duì)象的引用*/
    13?
    14?if(str3?== ?str5){
    15???????System.out.println("str3?==?str5"
    );
    16?
    }
    17?else
    {
    18???????System.out.println("str3?!=?str5"
    );
    19?
    }
    20?//輸出:str3?==?str5?????

    21?
    22?str4?=?str4.intern();??????/*此時(shí),JVM發(fā)現(xiàn)滯留字符串中已經(jīng)存在“world”對(duì)象,因此返回str3指向的對(duì)象給str4,即str3和str4是指向同一個(gè)對(duì)象的引用*/
    23? ?????
    24?if(str3?==
    ?str4){
    25???????System.out.println("after?intern()?str3?==?str4"
    );
    26?
    }
    27?else
    {
    28???????System.out.println("after?intern()?str3?!=?str4"
    );
    29?
    }
    30?//輸出:after?intern()?str3?==?str4

    31?
    32?

    結(jié)論: 如果使用new關(guān)鍵字創(chuàng)建了字符串變量,則盡可能使用intern()方法。

    上面的例子執(zhí)行正是用到了string對(duì)象的不可變性質(zhì)。既然string對(duì)象一旦創(chuàng)建就不可以改變,那么多個(gè) 引用指向同一個(gè)對(duì)象就不會(huì)對(duì)彼此產(chǎn)生影響。


    三.字符串連接

      你可以使用+操作符或者String.concat()或者StringBuffer.append()等辦法來(lái)連接多個(gè)字符串,哪 一種方法性能最佳?

    如何選擇取決于兩種情景:
    第一種情景:需要連接的字符串是在編譯期間決定的,還是在運(yùn)行期間決定。
    第二種情景:你使用的是 StringBuffer還是String。

    通常程序員會(huì)認(rèn)為StringBuffer.append()方法會(huì)優(yōu)于+操作符或 String.concat()方法,但是在一些特定 的情況下這個(gè)假想是不成立的。
    ?

    1) 第一種情景:編譯期間決定VS
    運(yùn)行期間決定

    ?1?//test?the?string?Concatenation
    ?2?long?startTime6?= ?System.currentTimeMillis();
    ?3?for(int?k=0;?k<100000;?k++
    ){
    ?4???????String?str6?=?"this?is?"?+?"a?test?"?+?"for?string?concatenation"
    ;
    ?5?
    }
    ?6?long?endTime6?=
    ?System.currentTimeMillis();
    ?7?System.out.println("string?concatenation?using?'+'?:?"?+?(endTime6?-?startTime6)?+?"?milli?seconds"
    );
    ?8?
    ??
    ?9?long?startTime7?=
    ?System.currentTimeMillis();
    10?for(int?k=0;?k<100000;?k++
    ){
    11???????String?str7?=?"this?is?"
    ;
    12???????str7.concat("a?test?"
    );
    13???????str7.concat("for?string?concatenation"
    );
    14?
    }
    15?long?endTime7?=
    ?System.currentTimeMillis();
    16?System.out.println("string?concatenation?using?concat()?:?"?+?(endTime7?-?startTime7)?+?"?milli?seconds"
    );
    17?

    18?long?startTime8?= ?System.currentTimeMillis();
    19?for(int?l=0;?l<100000;?l++
    ){
    20???????StringBuffer?sb8?=?new
    ?StringBuffer();
    21???????sb8.append("this?is?"
    );
    22???????sb8.append("a?test?"
    );
    23???????sb8.append("for?string?concatenation"
    );
    24?
    }
    25?long?endTime8?=
    ?System.currentTimeMillis();
    26?System.out.println("string?concatenation?using?append()?:?"?+?(endTime8?-?startTime8)?+?"?milli?seconds");

    上面代碼的輸出結(jié)果:

    string?concatenation?using? ' + ' ?:? 0 ?milli?seconds
    string?concatenation?using?concat()?:?
    31
    ?milli?seconds
    string?concatenation?using?append()?:?
    62 ?milli?seconds

    很有趣,+操作符比StringBuffer.append()方法要快. Why?
    ?
    這是因?yàn)榫幾g器對(duì)簡(jiǎn)單的字符串進(jìn)行了優(yōu)化。即使使用new關(guān)鍵字來(lái)創(chuàng)建String對(duì)象的時(shí)候也是如此。例 如,

    編譯前:
    String str6 = "this is " + "a test " + "for string concatenation";
    編譯后:
    String str6 = "this is a test for string concatenation";

    這里String對(duì)象在編譯期間就決定了而StringBuffer對(duì)象是在運(yùn)行期間決定的。運(yùn)行期間決定需要額外的 開銷。

    結(jié)論: 如果字符串在編譯期間就可以決定它的值,則字符串拼接的時(shí)候, “+”操作符效率更高,簡(jiǎn)單的 認(rèn)為append()效率高于“+”是錯(cuò)誤的。
    ?

    2) 第二種情景:StringBufferVS
    String

    ?1?//string?concatenation?using?'+='
    ?2?long?startTime9?= ?System.currentTimeMillis();
    ?3?String?str9?=?"hello"
    ;
    ?4?for(int?i=0;?i<10000;?i++
    ){
    ?5???????str9?+=?"hello"
    ;
    ?6?
    }
    ?7?long?endTime9?=
    ?System.currentTimeMillis();
    ?8?System.out.println("string?concatenation?using?'+='?:?"?+?(endTime9?-?startTime9)?+?"?milli?seconds"
    );
    ?9?
    ??
    10?//string?concatenation?using?append()

    11?long?startTime10?= ?System.currentTimeMillis();
    12?StringBuffer?sb10?=?new?StringBuffer("hello"
    );
    13?for(int?i=0;?i<10000;?i++
    ){
    14???????sb10.append("hello"
    );
    15?
    }
    16?long?endTime10?=
    ?System.currentTimeMillis();
    17?System.out.println("string?concatenation?using?append()?:?"?+?(endTime10?-?startTime10)?+?"?milli?seconds");


    上面代碼的輸出結(jié)果:

    string?concatenation?using? ' += ' ?:? 3094 ?milli?seconds
    string?concatenation?using?append()?:?
    16 ?milli?seconds

    結(jié)論: 避免使用“+=”來(lái)構(gòu)造字符串

    雖然兩者都是在運(yùn)行期間決定字符串對(duì)象,但是使用+=操作符會(huì)產(chǎn)生更多的臨時(shí)對(duì)象。

    在上例中,由于String類是不可變的,所以進(jìn)行字符串拼接的時(shí)候,每循環(huán)一次就會(huì)產(chǎn)生臨時(shí)對(duì)象來(lái)保存 str9和“hello”的值,之后創(chuàng)建一個(gè)臨時(shí)的StringBuffer對(duì)象,并調(diào)用其append()方法來(lái)完成字符串的 拼接,最后調(diào)用toString()方法,將臨時(shí)StringBuffer對(duì)象轉(zhuǎn)為String再賦值給str9。此時(shí)str9已經(jīng)改變 ,指向了新的對(duì)象。


    3) 第三種情景:設(shè)置StringBuffer的容量來(lái)提升性能

    ?1?long?startTime10?= ?System.currentTimeMillis();
    ?2?StringBuffer?sb10?=?new?StringBuffer("hello"
    );
    ?3?for(int?i=0;?i<10000;?i++
    ){
    ?4???????sb10.append("hello"
    );
    ?5?
    }
    ?6?long?endTime10?=
    ?System.currentTimeMillis();
    ?7?System.out.println("string?concatenation?using?append()?:?"?+?(endTime10?-?startTime10)?+?"?milli?seconds"
    );
    ?8?
    ??
    ?9?//set?the?StringBuffer?capacity

    10?long?startTime11?= ?System.currentTimeMillis();
    11?StringBuffer?sb11?=?new?StringBuffer("hello"
    );
    12?sb11.ensureCapacity(10000
    );
    13?for(int?i=0;?i<10000;?i++
    ){
    14???????sb11.append("hello"
    );
    15?
    }
    16?long?endTime11?=
    ?System.currentTimeMillis();
    17?System.out.println("string?concatenation?using?append()?after?set?the?StringBuffer?capacity?:?"?+?(endTime11?-?startTime11)?+?"?milli?seconds");


    ? 上面代碼的輸出結(jié)果:

    string?concatenation?using?append()?:? 16 ?milli?seconds
    string?concatenation?using?append()?after?set?the?StringBuffer?capacity?:?
    0 ?milli?seconds

    結(jié)論:
    聲明StringBuffer對(duì)象的時(shí)候,指定合適的capacity,會(huì)提升程序性能。

    1)使用StringBuffer的構(gòu)造函數(shù)來(lái)設(shè)定它的初始化容量:StringBuffer(int length)
    2)使用ensureCapacity(int minimumcapacity)方法在StringBuffer對(duì)象創(chuàng)建之后設(shè)置它的容量。

    首先我們看看StringBuffer的缺省行為,然后再找出一條更好的提升性能的途徑。
    ?
    StringBuffer的缺省行為:
      StringBuffer在內(nèi)部維護(hù)一個(gè)字符數(shù)組,當(dāng)你使用缺省的構(gòu)造函數(shù)來(lái)創(chuàng)建StringBuffer對(duì)象的時(shí)候, StringBuffer的容量被初始化為16個(gè)字符,也就是說(shuō)缺省容量就是16個(gè)字符。當(dāng)StringBuffer達(dá)到最大容 量的時(shí)候,它會(huì)將自身容量增加到當(dāng)前的2倍再加2,也就是(2*舊值+2)。
      如果你使用缺省值,初始化之后接著往里面追加字符,在你追加到第17(原文是16,其實(shí)是錯(cuò)誤的,因?yàn)樵谧芳拥降?6個(gè)字符的時(shí)候,容量不會(huì)發(fā)生變化,很抱歉,以后會(huì)更嚴(yán)謹(jǐn)一些^+^)個(gè)字符的時(shí)候它會(huì)將容量增加 到34(2*16+2),當(dāng)追加到34個(gè)字符的時(shí)候就會(huì)將容量增加到70(2*34+2)。無(wú)論何事只要StringBuffer 到達(dá)它的最大容量它就不得不創(chuàng)建一個(gè)新的字符數(shù)組然后重新將舊字符和新字符都拷貝一遍。所以給 StringBuffer設(shè)置一個(gè)合理的初始化容量值,會(huì)提升程序的性能。

    但是為什么容量變化的時(shí)候是2*舊值+2呢?有誰(shuí)能告訴我么?查資料的時(shí)候沒(méi)有找到

    ?

    附:查資料的過(guò)程中發(fā)現(xiàn)了jdk 5.0還提供了StringBuilder類,我是沒(méi)有用過(guò)。不過(guò)也介紹一下好了。( 來(lái)源 JavaWorld ):

    ?????? Java.lang.StringBuffer 線程安全的可變字符序列。類似于 String 的字符串緩沖區(qū),但不能修 改。可將字符串緩沖區(qū)安全地用于多個(gè)線程。可以在必要時(shí)對(duì)這些方法進(jìn)行同步,因此任意特定實(shí)例上的 所有操作就好像是以串行順序發(fā)生的,該順序與所涉及的每個(gè)線程進(jìn)行的方法調(diào)用順序一致。

    ?????? 每個(gè)字符串緩沖區(qū)都有一定的容量。只要字符串緩沖區(qū)所包含的字符序列的長(zhǎng)度沒(méi)有超出此容量 ,就無(wú)需分配新的內(nèi)部緩沖區(qū)數(shù)組。如果內(nèi)部緩沖區(qū)溢出,則此容量自動(dòng)增大。從 JDK 5.0 開始,為該 類增添了一個(gè)單個(gè)線程使用的等價(jià)類,即 StringBuilder 。與該類相比,通常應(yīng)該優(yōu)先使用 StringBuilder 類,因?yàn)樗С炙邢嗤牟僮鳎捎谒粓?zhí)行同步,所以速度更快。 但是如果將 StringBuilder 的實(shí)例用于多個(gè)線程是不安全的。需要這樣的同步,則建議使用 StringBuffer 。


    ?



    馬嘉楠
    jianan.ma@gmail.com

    posted on 2006-09-14 17:25 馬嘉楠 閱讀(3045) 評(píng)論(6)  編輯  收藏

    FeedBack:
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-15 09:24 | Liam
    不錯(cuò),很好,收益了~~期待下次的內(nèi)容  回復(fù)  更多評(píng)論
      
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-15 11:21 | themax
    不錯(cuò).謝謝!  回復(fù)  更多評(píng)論
      
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-15 12:01 | 吳加前
    我用過(guò)StringBuilder這個(gè)類,在程序中確定沒(méi)有用到多線程的時(shí)候,用StringBuilder比StringBuffer的效率要高.  回復(fù)  更多評(píng)論
      
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-15 12:03 | xinheqishi
    收益良多,謝謝了。  回復(fù)  更多評(píng)論
      
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-19 11:48 | 盧翔
    牛X,收了  回復(fù)  更多評(píng)論
      
    # re: 如何讓你的程序運(yùn)行的更快(1)---String VS StringBuffer
    2006-09-22 20:43 | ljcyu
    第一個(gè)測(cè)試有問(wèn)題 ,你運(yùn)行的次數(shù)太少了,很受操作系統(tǒng)當(dāng)時(shí)狀況的影響 。最好是運(yùn)行1000次,然后取平均數(shù),我這里得到的一個(gè)是3.734,一個(gè)是1.157。   回復(fù)  更多評(píng)論
      

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产色在线|亚洲| 视频一区在线免费观看| 免费在线人人电影网| 日本一卡精品视频免费| 青青青国产免费一夜七次郎| 亚洲无人区一区二区三区| 亚洲国产夜色在线观看| 一级a性色生活片久久无少妇一级婬片免费放 | 久久亚洲AV成人出白浆无码国产| 亚洲欧美精品午睡沙发| 任你躁在线精品免费| 日本免费一区二区三区最新vr| 久久亚洲精品无码| 国产成人亚洲精品电影| 99久久久国产精品免费牛牛 | 国产AV日韩A∨亚洲AV电影| 国产激情免费视频在线观看 | 亚洲欧洲无码一区二区三区| 精品国产一区二区三区免费| 在线精品免费视频| 亚洲AV无码专区国产乱码电影| 久久亚洲中文无码咪咪爱| 99久久久国产精品免费牛牛 | 在线亚洲午夜理论AV大片| 亚洲日韩中文字幕一区| 国产免费无码AV片在线观看不卡| 日本高清免费不卡在线| 亚洲精品456在线播放| yy一级毛片免费视频| 好吊妞998视频免费观看在线| 亚洲AV无码成人精品区在线观看 | 午夜亚洲av永久无码精品| 亚洲一区二区三区久久久久| 青青操视频在线免费观看| 免费a级毛片永久免费| 亚洲国产精品成人精品小说| 成人影片一区免费观看| 亚洲精品动漫人成3d在线| 亚洲av日韩精品久久久久久a| 国产免费一区二区三区| 亚洲AV区无码字幕中文色|