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

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

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

    隨筆-72  評論-63  文章-0  trackbacks-0
    String和StringBuffer之概覽
      非可變對象一旦創(chuàng)建之后就不能再被改變,可變對象則可以在創(chuàng)建之后被改變。String對象是非可變對象,StringBuffer對象則是可變對象。為獲得更佳的性能你需要根據(jù)實際情況小心謹(jǐn)慎地選擇到底使用這兩者中的某一個。下面的話題會作詳細(xì)的闡述。(注意:這個章節(jié)假設(shè)讀者已經(jīng)具備Java的String和StringBuffer的相關(guān)基礎(chǔ)知識。)
    ?
    創(chuàng)建字符串的較佳途徑
    你可以按照以下方式創(chuàng)建字符串對象:
    1.?String?s1?=?"hello";?
    ????String?s2?=?"hello";?
    2.?String?s3?=?new?String("hello");
    ????String?s4?=?new?String("hello");
    ?
    上面哪種方式會帶來更好的性能呢?下面的代碼片斷用來測量二者之間的區(qū)別。

    StringTest1.java
    package?com.performance.string;
    /**?This?class?shows?the?time?taken?for?creation?of
    ?*??String?literals?and?String?objects.
    ?*/
    public?class?StringTest1?{
    public?static?void?main(String[]?args){
    ????//?create?String?literals
    ????long?startTime?=?System.currentTimeMillis();
    ????for(int?i=0;i<50000;i++){
    ????String?s1?=?"hello";
    ????String?s2?=?"hello";
    ????}
    ????long?endTime?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?creation?of?String?literals?:?"
    ??????????????????+?(endTime?-?startTime)?+?"?milli?seconds"?);
    ????//?create?String?objects?using?'new'?keyword???????
    ????long?startTime1?=?System.currentTimeMillis();
    ????for(int?i=0;i<50000;i++){
    ????String?s3?=?new?String("hello");
    ????String?s4?=?new?String("hello");
    ????}
    ????long?endTime1?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?creation?of?String?objects?:?"
    ??????????????????+?(endTime1?-?startTime1)+"?milli?seconds");
    ????}
    }
    這段代碼的輸出:
    Time?taken?for?creation?of?String?literals?:?0?milli?seconds
    Time?taken?for?creation?of?String?objects?:?170?milli?seconds
    ?
    JVM是怎樣處理字符串的呢?
      Java虛擬機會維護(hù)一個內(nèi)部的滯留字符串對象的列表(唯一字符串的池)來避免在堆內(nèi)存中產(chǎn)生重復(fù)的String對象。當(dāng)JVM從class文件里加載字符串字面量并執(zhí)行的時候,它會先檢查一下當(dāng)前的字符串是否已經(jīng)存在于滯留字符串列表,如果已經(jīng)存在,那就不會再創(chuàng)建一個新的String對象而是將引用指向已經(jīng)存在的String對象,JVM會在內(nèi)部為字符串字面量作這種檢查,但并不會為通過new關(guān)鍵字創(chuàng)建的String對象作這種檢查。當(dāng)然你可以明確地使用String.intern()方法強制JVM為通過?new關(guān)鍵字創(chuàng)建的String對象作這樣的檢查。這樣可以強制JVM檢查內(nèi)部列表而使用已有的String對象。
      所以結(jié)論是,JVM會內(nèi)在地為字符串字面量維護(hù)一些唯一的String對象,程序員不需要為字符串字面量而發(fā)愁,但是可能會被一些通過?new關(guān)鍵字創(chuàng)建的String對象而困擾,不過他們可以使用intern()方法來避免在堆內(nèi)存上創(chuàng)建重復(fù)的String對象來改善Java的運行性能。下一小節(jié)會向大家展示更多的信息。
    ?
    下圖展示了未使用intern()方法來創(chuàng)建字符串的情況。
    ?
    string_creating_without_intern()?method
      你可以自己使用==操作符和String.equals()方法來編碼測試上面提到的區(qū)別。==操作符會返回true如果一些引用指向一個相同的對象但不會判斷String對象的內(nèi)容是否相同;String.equals()方法會返回true如果被操作的String對象的內(nèi)容相同。對于上面的代碼會有s1==s2,因為s1和s2兩個引用指向同一個對象,對于上面的代碼,s3.equals(s4)會返回true因為兩個對象的內(nèi)容都一樣為”hello”。你可以從上圖看出這種機制。在這里有三個獨立的包含了相同的內(nèi)容(”hello”)的對象,實際上我們不需要這么三個獨立的對象——?因為要運行它們的話既浪費時間又浪費內(nèi)存。
    ?
      那么怎樣才能確保String對象不會重復(fù)呢?下一個話題會涵蓋對于內(nèi)建String機制的興趣。
    ?
    滯留字符串的優(yōu)化作用
      同一個字符串對象被重復(fù)地創(chuàng)建是不必要的,String.intern?()方法可以避免這種情況。下圖說明了String.intern()方法是如何工作的,String.intern()方法檢查字符串對象的存在性,如果需要的字符串對象已經(jīng)存在,那么它會將引用指向已經(jīng)存在的字符串對象而不是重新創(chuàng)建一個。下圖描繪了使用了intern()方法的字符串字面量和字符串對象的創(chuàng)建情況。
    ?
    string_creating_with_intern()?method
    下面的例程幫助大家了解String.intern()方法的重要性。
    StringTest2.java
    ?
    package?com.performance.string;
    //?This?class?shows?the?use?of?intern()?method?to?improve?performance
    public?class?StringTest2?{
    public?static?void?main(String[]?args){
    ????//?create?String?references?like?s1,s2,s3...so?on..
    ????String?variables[]?=?new?String[50000];
    ????for(?int?i=0;i<variables.length;i++){
    ????????variables[i]?=?"s"+i;
    ????}
    ????//?create?String?literals
    ????long?startTime0?=?System.currentTimeMillis();
    ????for(int?i=0;i<variables.length;i++){
    ????????variables[i]?=?"hello";
    ????}
    ????long?endTime0?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?creation?of?String?literals?:?"
    ?????????????????????????+?(endTime0?-?startTime0)?+?"?milli?seconds"?);
    ????//?create?String?objects?using?'new'?keyword???????
    ????long?startTime1?=?System.currentTimeMillis();
    ????for(int?i=0;i<variables.length;i++){
    ????????variables[i]?=?new?String("hello");
    ????}
    ????long?endTime1?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?creation?of?String?objects?with?'new'?key?word?:?"
    ????????????????????????+?(endTime1?-?startTime1)+"?milli?seconds");
    ????//?intern?String?objects?with?intern()?method???
    ????long?startTime2?=?System.currentTimeMillis();
    ????for(int?i=0;i<variables.length;i++){
    ????????variables[i]?=?new?String("hello");
    ????????variables[i]?=?variables[i].intern();
    ????}
    ????long?endTime2?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?creation?of?String?objects?with?intern():?"
    ????????????????????????+?(endTime2?-?startTime2)+"?milli?seconds");
    ????}
    }
    這是上面那段代碼的輸出結(jié)果:
    Time?taken?for?creation?of?String?literals?:?0?milli?seconds
    Time?taken?for?creation?of?String?objects?with?'new'?key?word?:?160?milli?seconds
    Time?taken?for?creation?of?String?objects?with?intern():?60?milli?seconds
    ?
    連接字符串時候的優(yōu)化技巧
      你可以使用+操作符或者String.concat()或者StringBuffer.append()等辦法來連接多個字符串,那一種辦法具有最佳的性能呢?
      如何作出選擇取決于兩種情景,第一種情景是需要連接的字符串是在編譯期決定的還是在運行期決定的,第二種情景是你使用的是?StringBuffer還是String。通常程序員會認(rèn)為StringBuffer.append()方法會優(yōu)于+操作符或?String.concat()方法,但是在一些特定的情況下這個假想是不成立的。
    ?
    1)?第一種情景:編譯期決定相對于運行期決定
    請看下面的StringTest3.java代碼和輸出結(jié)果。

    package?com.performance.string;
    /**?This?class?shows?the?time?taken?by?string?concatenation?at?compile?time?and?run?time.*/
    public?class?StringTest3?{
    ??public?static?void?main(String[]?args){
    ????//Test?the?String?Concatination
    ????long?startTime?=?System.currentTimeMillis();
    ????for(int?i=0;i<5000;i++){
    ????String?result?=?"This?is"+?"testing?the"+?"difference"+?"between"+
    ????????????"String"+?"and"+?"StringBuffer";
    ????}
    ????long?endTime?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?string?concatenation?using?+?operator?:?"
    ?????????+?(endTime?-?startTime)+?"?milli?seconds");
    ????//Test?the?StringBuffer?Concatination
    ????long?startTime1?=?System.currentTimeMillis();
    ????for(int?i=0;i<5000;i++){
    ????StringBuffer?result?=?new?StringBuffer();
    ?????????result.append("This?is");
    ????????result.append("testing?the");
    ????????result.append("difference");
    ????????result.append("between");
    ???????result.append("String");
    ???????result.append("and");
    ???????result.append("StringBuffer");
    ?????}
    ????long?endTime1?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?String?concatenation?using?StringBuffer?:?"
    ???????????+?(endTime1?-?startTime1)+?"?milli?seconds");
    ??}
    }
    這是上面的代碼的輸出結(jié)果:
    Time?taken?for?String?concatenation?using?+?operator?:?0?milli?seconds
    Time?taken?for?String?concatenation?using?StringBuffer?:?50?milli?seconds
    很有趣地,+操作符居然比StringBuffer.append()方法要快,為什么呢?
    ?
      這里編譯器的優(yōu)化起了關(guān)鍵作用,編譯器像下面舉例的那樣簡單地在編譯期連接多個字符串。它使用編譯期決定取代運行期決定,在你使用new關(guān)鍵字來創(chuàng)建String對象的時候也是如此。
    ?
    編譯前:
    String?result?=?"This?is"+"testing?the"+"difference"+"between"+"String"+"and"+"StringBuffer";
    編譯后:
    String?result?=?"This?is?testing?the?difference?between?String?and?StringBuffer";

    這里String對象在編譯期就決定了而StringBuffer對象是在運行期決定的。運行期決定需要額外的開銷當(dāng)字符串的值無法預(yù)先知道的時候,編譯期決定作用于字符串的值可以預(yù)先知道的時候,下面是一個例子。
    ?
    編譯前:
    public?String?getString(String?str1,String?str2)?{
    ????return?str1+str2;
    }
    編譯后:
    return?new?StringBuffer().append(str1).append(str2).toString();
    運行期決定需要更多的時間來運行。
    ?
    2)?第二種情景:使用StringBuffer取代String
    看看下面的代碼你會發(fā)現(xiàn)與情景一相反的結(jié)果——連接多個字符串的時候StringBuffer要比String快。
    StringTest4.java
    ?
    package?com.performance.string;
    /**?This?class?shows?the?time?taken?by?string?concatenation
    using?+?operator?and?StringBuffer??*/
    public?class?StringTest4?{
    ?public?static?void?main(String[]?args){
    ????//Test?the?String?Concatenation?using?+?operator
    ????long?startTime?=?System.currentTimeMillis();
    ????String?result?=?"hello";
    ????for(int?i=0;i<1500;i++){
    ????????result?+=?"hello";
    ????}
    ????long?endTime?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?string?concatenation?using?+?operator?:?"
    ??????????????????+?(endTime?-?startTime)+?"?milli?seconds");
    ????//Test?the?String?Concatenation?using?StringBuffer
    ????long?startTime1?=?System.currentTimeMillis();
    ????StringBuffer?result1?=?new?StringBuffer("hello");
    ????for(int?i=0;i<1500;i++){
    ????????result1.append("hello");
    ????}
    ????long?endTime1?=?System.currentTimeMillis();
    ????System.out.println("Time?taken?for?string?concatenation?using?StringBuffer?:??"
    ??????????????????+?(endTime1?-?startTime1)+?"?milli?seconds");
    ????}
    }
    這是上面的代碼的輸出結(jié)果:
    Time?taken?for?string?concatenation?using?+?operator?:?280?milli?seconds
    Time?taken?for?String?concatenation?using?StringBuffer?:?0?milli?seconds
    看得出StringBuffer.append()方法要比+操作符要快得多,為什么呢?

      原因是兩者都是在運行期決定字符串對象,但是+操作符使用不同于StringBuffer.append()的規(guī)則通過String和StringBuffer來完成字符串連接操作。(譯注:什么樣的規(guī)則呢?)
    ?
    借助StringBuffer的初始化過程的優(yōu)化技巧
      你可以通過StringBuffer的構(gòu)造函數(shù)來設(shè)定它的初始化容量,這樣可以明顯地提升性能。這里提到的構(gòu)造函數(shù)是StringBuffer(int?length),length參數(shù)表示當(dāng)前的StringBuffer能保持的字符數(shù)量。你也可以使用ensureCapacity(int?minimumcapacity)方法在StringBuffer對象創(chuàng)建之后設(shè)置它的容量。首先我們看看StringBuffer的缺省行為,然后再找出一條更好的提升性能的途徑。
    ?
    StringBuffer的缺省行為:
      StringBuffer在內(nèi)部維護(hù)一個字符數(shù)組,當(dāng)你使用缺省的構(gòu)造函數(shù)來創(chuàng)建StringBuffer對象的時候,因為沒有設(shè)置初始化字符長度,StringBuffer的容量被初始化為16個字符,也就是說缺省容量就是16個字符。當(dāng)StringBuffer達(dá)到最大容量的時候,它會將自身容量增加到當(dāng)前的2倍再加2,也就是(2*舊值+2)。
      如果你使用缺省值,初始化之后接著往里面追加字符,在你追加到第16個字符的時候它會將容量增加到34(2*16+2),當(dāng)追加到34個字符的時候就會將容量增加到70(2*34+2)。無論何事只要StringBuffer到達(dá)它的最大容量它就不得不創(chuàng)建一個新的字符數(shù)組然后重新將舊字符和新字符都拷貝一遍——這也太昂貴了點。所以總是給StringBuffer設(shè)置一個合理的初始化容量值是錯不了的,這樣會帶來立竿見影的性能增益。
      我利用兩個StringBuffer重新測試了上面的StringTest4.java代碼,一個未使用初始化容量值而另一個使用了。這次我追加了50000個’hello’對象沒有使用+操作符。區(qū)別是我使用StringBuffer(250000)的構(gòu)造函數(shù)來初始化第二個?StringBuffer了。
    ?
    輸出結(jié)果如下:
    Time?taken?for?String?concatenation?using?StringBuffer?with?out?setting?size:?280?milli?seconds
    Time?taken?for?String?concatenation?using?StringBuffer?with?setting?size:?0?milli?seconds
    StringBuffer初始化過程的調(diào)整的作用由此可見一斑。所以,使用一個合適的容量值來初始化StringBuffer永遠(yuǎn)都是一個最佳的建議。
    ?
    關(guān)鍵點
    1.?無論何時只要可能的話使用字符串字面量來常見字符串而不是使用new關(guān)鍵字來創(chuàng)建字符串。
    2.?無論何時當(dāng)你要使用new關(guān)鍵字來創(chuàng)建很多內(nèi)容重復(fù)的字符串的話,請使用String.intern()方法。
    3.?+操作符會為字符串連接提供最佳的性能——當(dāng)字符串是在編譯期決定的時候。
    4.?如果字符串在運行期決定,使用一個合適的初期容量值初始化的StringBuffer會為字符串連接提供最佳的性能

    http://java.chinaitlab.com/JDK/364481.html
    posted on 2006-07-13 17:47 船長 閱讀(630) 評論(0)  編輯  收藏 所屬分類: J2EE
    主站蜘蛛池模板: 亚洲成年轻人电影网站www| 亚洲精品无码AV中文字幕电影网站| 亚洲精品无码av天堂| 日韩色日韩视频亚洲网站| 曰皮全部过程视频免费国产30分钟 | 一级成人毛片免费观看| 国产免费黄色大片| 免费一级毛片在线播放放视频| 日本视频免费在线| 福利片免费一区二区三区| 免费国产成人午夜私人影视| 精品成人一区二区三区免费视频| 大胆亚洲人体视频| 国产精品视频全国免费观看| 亚洲AV无码一区二区乱子伦| 最近免费中文字幕高清大全| 亚洲一区电影在线观看| 大陆一级毛片免费视频观看| 亚洲暴爽av人人爽日日碰| 免费福利资源站在线视频| 亚洲毛片av日韩av无码 | 日韩免费高清播放器| 亚洲va久久久噜噜噜久久狠狠| 久久这里只精品99re免费| 亚洲精品国产手机| 免费的一级黄色片| 成年免费a级毛片| 亚洲成a人片在线观看无码| 1000部拍拍拍18勿入免费视频软件 | 好男人视频社区精品免费| 精品国产亚洲一区二区三区在线观看| 亚洲精品老司机在线观看| 特级做A爰片毛片免费看无码 | 国产在线观看麻豆91精品免费| 亚洲欧美黑人猛交群| 亚洲成aⅴ人片久青草影院| 暖暖在线视频免费视频| 亚洲精品美女久久久久久久| 亚洲中文字幕无码日韩| 亚洲国产精品免费观看| WWW国产成人免费观看视频|