熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

如何讓你的程序運行的更快

2022-06-13   來源: Java核心技術 

  在總體的框架設計確定以後多注意一些編程細節積少成多可以獲得更佳的性能讓程序跑的更快!
   
    前些天同事優化代碼時提到了String和StringBuffer仔細想想發現自己也就是知道個大概所以查了 一下資料文章中有錯誤的地方你可一定要告訴我啊呵呵^+^
   
    一介紹
   
    String:非可變類(immutable)一旦創建就不能再被改變
   
    StringBuffer:可變類創建之後可以被改變
   
    何謂非可變類?
   
    簡單說非可變類的實例是不能被修改的每個實例中包含的信息都必須在該實例創建的時候就提供出來 並且在對象的整個生存周期內固定不變
   
    非可變類好處狀態單一對象簡單便於維護通常是線程安全的用戶可以共享非可變對象甚至可 以共享它們的內部信息
   
    二創建字符串
   
    兩種方法
   
    String s = hello;
   
    String s = new String(hello
   
    哪種方式性能更好?
   
    例:
   
    //create String without new keyword
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
   
   
    for(inti=;i<;i++){
   
          String str=
   
    hello;
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(create String without new keyword :
   
    +(endTimestartTime)+
   
    milli seconds
   
   
   
   
   
    //create String with new keyword
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
   
   
    for(inti=;i<;i++){
   
          String str=
   
    newString(hello
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(create String with new keyword :
   
    +(endTimestartTime)+
   
    milli seconds
   
    輸出結果為(注程序的輸出結果也許和你的結果不同但是總體趨勢應該是一致的)
   
    create String withoutnewkeyword :milli seconds
   
    create String withnewkeyword :milli seconds
   
    結論創建字符串變量時盡可能不使用new關鍵字
   
    說明
   
    雖然兩個語句都是返回一個String對象的引用但是jvm對兩者的處理方式是不一樣的
   
    對於第一種不用new關鍵字創建String對象
   
    JVM首先會在內部維護的滯留字符串中通過String的equels方法查找是對象池中是否存放有該 String對象如果有返回已有的String對象給用戶而不會在heap中重新創建一個新的String對象如 果對象池中沒有該String對象JVM則在heap中創建新的String對象將其引用返回給用戶同時將該引 用添加至滯留字符串中
   
    對於第二種使用new關鍵字創建String對象
   
    JVM會馬上在heap中創建一個String對象然後將該對象的引用返回給用戶JVM是不會主動把該 對象放到滯留字符串裡面的除非程序調用 String的intern()方法
   
    JVM為字符串自變量維護一些唯一的String對象程序員不需要為字符串自變量而發愁但是使用new關鍵 字可能會在內存中創建重復的String對象你不必為此而煩惱intern()方法可以幫你解決問題
   
    Stringintern()檢查字符串對象的存在性如果需要的字符串對象已經存在那麼它會將引用指向已 經存在的字符串對象而不是重新創建一個
   
    例:
   
    String str=
   
    world;      /*JVM在滯留字符串中找不到值為world的字符串就在堆上創建一個string對象並將該對象的引用加入到滯留字符串中*/
   
   
   
    String str=
   
    newString(world      /*JVM在堆上重新創建一個值為world的字符串此時堆上有兩個值為world的字符串*/
   
   
   
    if(str==str){
   
          Systemoutprintln(str == str
   
      }
   
   
   
    else{
   
          Systemoutprintln(str != str
   
      }
   
   
   
    //輸出str != str
   
   
   
    String str=
   
    world;      /*JVM在發現滯留字符串中存在world對象因此返回str指向的對象給str即str和str是指向同一個對象的引用*/
   
   
   
      if(str==str){
   
      Systemoutprintln(str == str
   
      }
   
      else{
   
      Systemoutprintln(str != str
   
      }
   
   
   
    //輸出str == str
   
   
   
    str=strintern()      /*此時JVM發現滯留字符串中已經存在world對象因此返回str指向的對象給str即str和str是指向同一個對象的引用*/
   
   
   
   
   
    if(str==str){
   
          Systemoutprintln(after intern() str == str
   
      }
   
   
   
    else{
   
          Systemoutprintln(after intern() str != str
   
      }
   
   
   
    //輸出after intern() str == str
   
   
   
   
   
    結論如果使用new關鍵字創建了字符串變量則盡可能使用intern()方法
   
    上面的例子執行正是用到了string對象的不可變性質既然string對象一旦創建就不可以改變那麼多個 引用指向同一個對象就不會對彼此產生影響
   
    三字符串連接
   
    你可以使用+操作符或者ncat()或者StringBufferappend()等辦法來連接多個字符串哪 一種方法性能最佳?
   
    如何選擇取決於兩種情景
   
    第一種情景需要連接的字符串是在編譯期間決定的還是在運行期間決定
   
    第二種情景你使用的是 StringBuffer還是String
   
    通常程序員會認為StringBufferappend()方法會優於+操作符或 ncat()方法但是在一些特定 的情況下這個假想是不成立的
   
    ) 第一種情景編譯期間決定VS運行期間決定
   
    //test the string Concatenation
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
   
   
    for(intk=; k<; k++){
   
          String str=
   
    this is
   
    +
   
    a test
   
    +
   
    for string concatenation;
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using + :
   
    +(endTimestartTime)+
   
    milli seconds
   
   
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
   
   
    for(intk=; k<; k++){
   
          String str=
   
    this is;
   
          ncat(a test
   
          ncat(for string concatenation
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using concat() :
   
    +(endTimestartTime)+
   
    milli seconds
   
   
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
   
   
    for(intl=; l<; l++){
   
          StringBuffer sb=
   
    newStringBuffer()
   
          sbappend(this is
   
          sbappend(a test
   
          sbappend(for string concatenation
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using append() :
   
    +(endTimestartTime)+
   
    milli seconds


   
    上面代碼的輸出結果
   
    string concatenation using+:milli seconds
   
    string concatenation using concat() :milli seconds
   
    string concatenation using append() :milli seconds
   
    很有趣+操作符比StringBufferappend()方法要快 Why?
   
    這是因為編譯器對簡單的字符串進行了優化即使使用new關鍵字來創建String對象的時候也是如此例 如
   
    編譯前
   
    String str = this is + a test + for string concatenation;
   
    編譯後
   
    String str = this is a test for string concatenation;
   
    這裡String對象在編譯期間就決定了而StringBuffer對象是在運行期間決定的運行期間決定需要額外的 開銷
   
    結論如果字符串在編譯期間就可以決定它的值則字符串拼接的時候 +操作符效率更高簡單的 認為append()效率高於+是錯誤的
   
    ) 第二種情景StringBufferVSString
   
    //string concatenation using +=
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
    String str=
   
    hello;
   
   
   
    for(inti=; i<; i++){
   
          str+=
   
    hello;
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using += :
   
    +(endTimestartTime)+
   
    milli seconds
   
   
   
   
   
    //string concatenation using append()
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
    StringBuffer sb=
   
    newStringBuffer(hello
   
   
   
    for(inti=; i<; i++){
   
          sbappend(hello
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using append() :
   
    +(endTimestartTime)+
   
    milli seconds
   
    上面代碼的輸出結果
   
    string concatenation using+=:milli seconds
   
    string concatenation using append() :milli seconds
   
    結論避免使用+=來構造字符串
   
    雖然兩者都是在運行期間決定字符串對象但是使用+=操作符會產生更多的臨時對象
   
    在上例中由於String類是不可變的所以進行字符串拼接的時候每循環一次就會產生臨時對象來保存 strhello的值之後創建一個臨時的StringBuffer對象並調用其append()方法來完成字符串的 拼接最後調用toString()方法將臨時StringBuffer對象轉為String再賦值給str此時str已經改變 指向了新的對象
   
    ) 第三種情景設置StringBuffer的容量來提升性能
   
    longstartTime=SystemcurrentTimeMillis()
   
    StringBuffer sb=
   
    newStringBuffer(hello
   
   
   
    for(inti=; i<; i++){
   
          sbappend(hello
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using append() :
   
    +(endTimestartTime)+
   
    milli seconds
   
   
   
   
   
    //set the StringBuffer capacity
   
   
   
    longstartTime=SystemcurrentTimeMillis()
   
    StringBuffer sb=
   
    newStringBuffer(hello
   
    sbensureCapacity(
   
   
   
    for(inti=; i<; i++){
   
          sbappend(hello
   
      }
   
   
   
    longendTime=SystemcurrentTimeMillis()
   
    Systemoutprintln(string concatenation using append() after set the StringBuffer capacity :
   
   
   
    milli seconds
   
    上面代碼的輸出結果
   
    string concatenation using append() :milli seconds
   
    string concatenation using append() after set the StringBuffer capacity :milli seconds
   
    結論
   
    聲明StringBuffer對象的時候指定合適的capacity會提升程序性能
   
    )使用StringBuffer的構造函數來設定它的初始化容量StringBuffer(int length)
   
    )使用ensureCapacity(int minimumcapacity)方法在StringBuffer對象創建之後設置它的容量
   
    首先我們看看StringBuffer的缺省行為然後再找出一條更好的提升性能的途徑
   
    StringBuffer的缺省行為
   
    StringBuffer在內部維護一個字符數組當你使用缺省的構造函數來創建StringBuffer對象的時候 StringBuffer的容量被初始化為個字符也就是說缺省容量就是個字符當StringBuffer達到最大容 量的時候它會將自身容量增加到當前的倍再加也就是(*舊值+
   
    如果你使用缺省值初始化之後接著往裡面追加字符在你追加到第(原文是其實是錯誤的因為在追加到第個字符的時候容量不會發生變化很抱歉以後會更嚴謹一些^+^)個字符的時候它會將容量增加 到*+當追加到個字符的時候就會將容量增加到*+無論何事只要StringBuffer 到達它的最大容量它就不得不創建一個新的字符數組然後重新將舊字符和新字符都拷貝一遍所以給 StringBuffer設置一個合理的初始化容量值會提升程序的性能
   
    但是為什麼容量變化的時候是*舊值+呢?有誰能告訴我麼?查資料的時候沒有找到
   
    附查資料的過程中發現了jdk 還提供了StringBuilder類我是沒有用過不過也介紹一下好了
   
    JavalangStringBuffer 線程安全的可變字符序列類似於 String 的字符串緩沖區但不能修 改可將字符串緩沖區安全地用於多個線程可以在必要時對這些方法進行同步因此任意特定實例上的 所有操作就好像是以串行順序發生的該順序與所涉及的每個線程進行的方法調用順序一致
   
    每個字符串緩沖區都有一定的容量只要字符串緩沖區所包含的字符序列的長度沒有超出此容量 就無需分配新的內部緩沖區數組如果內部緩沖區溢出則此容量自動增大從 JDK 開始為該 類增添了一個單個線程使用的等價類即 StringBuilder 與該類相比通常應該優先使用 StringBuilder 類因為它支持所有相同的操作但由於它不執行同步所以速度更快 但是如果將 StringBuilder 的實例用於多個線程是不安全的需要這樣的同步則建議使用 StringBuffer


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26749.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.