java提升篇(十四)-----字符串

      能夠證實,字符串操做是計算機程序設計中最多見的行爲。html

1、String

      首先咱們要明確,String並非基本數據類型,而是一個對象而且是不可變的對象。查看源碼就會發現String類爲final型的(固然也不可被繼承),並且經過查看JDK文檔會發現幾乎每個修改String對象的操做,實際上都是建立了一個全新的String對象。java

      字符串爲對象,那麼在初始化以前,它的值爲null,到這裏就有必要提下」」、null、new String()三者的區別。null 表示string尚未new ,也就是說對象的引用尚未建立,也沒有分配內存空間給他,而」」、new String()則說明了已經new了,只不過內部爲空,可是它建立了對象的引用,是須要分配內存空間的。打個比方:一個空玻璃杯,你不能說它裏面什麼都沒有,由於裏面有空氣,固然也能夠把它弄成真空,null與" "、new String()的區別就象真空與空氣同樣。正則表達式

      在字符串中存在一個很是特殊的地方,那就是字符串池。每當咱們建立一個字符串對象時,首先就會檢查字符串池中是否存在面值相等的字符串,若是有,則再也不建立,直接放回字符串池中對該對象的引用,若沒有則建立而後放入到字符串池中而且返回新建對象的引用。這個機制是很是有用的,由於能夠提升效率,減小了內存空間的佔用。因此在使用字符串的過程當中,推薦使用直接賦值(即String s=」aa」),除非有必要纔會新建一個String對象(即String s = new String(」aa」))。編程

      對於字符串的使用無非就是這幾個方面:數組

      一、字符串比較安全

         equals()   ------判斷內容是否相同。多線程

         compareTo() ------判斷字符串的大小關係。app

         compareToIgnoreCase(String int)    ------在比較時忽略字母大小寫。ide

         == ------判斷內容與地址是否相同。測試

         equalsIgnoreCase() ------忽略大小寫的狀況下判斷內容是否相同。

         reagionMatches() ------對字符串中的部份內容是否相同進行比較(詳情請參考API)。

      二、字符串查找

          charAt(int index) ------返回指定索引index位置上的字符,索引範圍從0開始。

          indexOf(String str)------從字符串開始檢索str,並返回第一次出現的位置,未出現返回-1。

          indexOf(String str,int fromIndex);------從字符串的第fromIndex個字符開始檢索str。

          lastIndexOf(String str)------查找最後一次出現的位置。

          lastIndexOf(String str,int fromIndex)----從字符串的第fromIndex個字符查找最後一次出現的位置。

          starWith(String prefix,int toffset)-----測試此字符串從指定索引開始的子字符串是否以指定前綴開始。

          starWith(String prefix)------測試此字符串是否以指定的前綴開始。

          endsWith(String suffix)------測試此字符串是否以指定的後綴結束。

      三、字符串截取

          public String subString(int beginIndex)------返回一個新的字符串,它是此字符串的一個子字符串。

          public String subString(int beginIndex,int endIndex)------返回的字符串是從beginIndex開始到endIndex-1的串。

     四、字符串替換

           public String replace(char oldChar,char newChar)。

           public String replace(CharSequence target,CharSequence replacement)------把原來的etarget子序列替換爲replacement序列,返回新串。

           public String replaceAll(String regex,String replacement)------用正則表達式實現對字符串的匹配。注意replaceAll第一個參數爲正則表達式,鄙人曾經深受其害。

     五、更多方法請參考API

2、StringBuffer

      StringBuffer和String同樣都是用來存儲字符串的,只不過因爲他們內部的實現方式不一樣,致使他們所使用的範圍不一樣,對於StringBuffer而言,他在處理字符串時,如果對其進行修改操做,它並不會產生一個新的字符串對象,因此說在內存使用方面它是優於String的。

      其實在使用方法,StringBuffer的許多方法和String類都差很少,所表示的功能幾乎如出一轍,只不過在修改時StringBuffer都是修改自身,而String類則是產生一個新的對象,這是他們之間最大的區別。

      同時StringBuffer是不能使用=進行初始化的,它必需要產生StringBuffer實例,也就是說你必須經過它的構造方法進行初始化。

      在StringBuffer的使用方面,它更加側重於對字符串的變化,例如追加、修改、刪除,相對應的方法:

      一、append():追加指定內容到當前StringBuffer對象的末尾,相似於字符串的鏈接,這裏StringBuffer對象的內容會發生改變。

      二、insert:該類方法主要是在StringBuffer對象中插入內容。

      三、delete:該類方法主要用於移除StringBuffer對象中的內容。

 

 

3、StringBuilder

      StringBuilder也是一個可變的字符串對象,他與StringBuffer不一樣之處就在於它是線程不安全的,基於這點,它的速度通常都比StringBuffer快。與StringBuffer同樣,StringBuider的主要操做也是append與insert方法。這兩個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符添加或插入到字符串生成器中。

      上面只是簡單的介紹了String、StringBuffer、StringBuilder,其實對於這三者咱們應該更加側重於他們只見到的區別,只有理清楚他們之間的區別纔可以更好的使用他們。

4、正確使用String、StringBuffer、StringBuilder

       咱們先看以下表格:

1111111

      這裏對於String是否爲線程安全,鄙人也不是很清楚,緣由:String不可變,全部的操做都是不可能改變其值的,是否存在線程安全一說還真很差說?可是若是硬要說線程是否安全的話,由於內容不可變,永遠都是安全的。

      在使用方面因爲String每次修改都須要產生一個新的對象,因此對於常常須要改變內容的字符串最好選擇StringBuffer或者StringBuilder.而對於StringBuffer,每次操做都是對StringBuffer對象自己,它不會生成新的對象,因此StringBuffer特別適用於字符串內容常常改變的狀況下。

      可是並非全部的String字符串操做都會比StringBuffer慢,在某些特殊的狀況下,String字符串的拼接會被JVM解析成StringBuilder對象拼接,在這種狀況下String的速度比StringBuffer的速度快。如:

      String name = 」I  」 + 」am 」 + 」chenssy 」 ;

      StringBuffer name = new StringBuffer(」I 」).append(」 am 」).append(」 chenssy 」);

     對於這兩種方式,你會發現第一種比第二種快太多了,在這裏StringBuffer的優點蕩然無存。其真實的緣由就在於JVM作了一下優化處理,其實String name = 」I 」 + 」am 」 + 」chenssy 」 ;在JVM眼中就是String name = 」I am  chenssy 」 ;這樣的方式對於JVM而言,真的是不要什麼時間。可是若是咱們在這個其中增長一個String對象,那麼JVM就會按照原來那種規範來構建String對象了。

      對於這三者使用的場景作以下歸納(參考:《編寫搞質量代碼:改善java程序的151個建議》):

      一、String:在字符串不常常變化的場景中可使用String類,如:常量的聲明、少許的變量運算等。

      二、StringBuffer:在頻繁進行字符串的運算(拼接、替換、刪除等),而且運行在多線程的環境中,則能夠考慮使用StringBuffer,例如XML解析、HTTP參數解析和封裝等。

      三、StringBuilder:在頻繁進行字符串的運算(拼接、替換、刪除等),而且運行在多線程的環境中,則能夠考慮使用StringBuffer,如SQL語句的拼裝、JSON封裝等(貌似這兩個我也是使用|StringBuffer)。

     更多有關於他們之間區別,請參考:http://www.cnblogs.com/zuoxiaolong/p/lang1.html。鄙人就不多此一舉了。

 

5、字符串拼接方式 

      對於字符串而言咱們常常是要對其進行拼裝處理的,在java中提升了三種拼裝的方法:+、concat()以及append()方法。這三者之間存在什麼區別呢?先看以下示例:

public class StringTest {
    
    /**
     * @desc 使用+、concat()、append()方法循環10W次
     * @author chenssy
     * @data 2013-11-16
     * @param args
     * @return void
     */
    public static void main(String[] args) {
        //+
        long start_01 = System.currentTimeMillis();
        String a = "a";
        for(int i = 0 ; i < 100000 ; i++){
            a += "b";
        }
        long end_01 = System.currentTimeMillis();
        System.out.println("  +   所消耗的時間:" + (end_01 - start_01) + "毫米");
        
        //concat()
        long start_02 = System.currentTimeMillis();
        String c = "c";
        for(int i = 0 ; i < 100000 ; i++){
            c = c.concat("d");
        }
        long end_02 = System.currentTimeMillis();
        System.out.println("concat所消耗的時間:" + (end_02 - start_02) + "毫米");
        
        //append
        long start_03 = System.currentTimeMillis();
        StringBuffer e = new StringBuffer("e");
        for(int i = 0 ; i < 100000 ; i++){
            e.append("d");
        }
        long end_03 = System.currentTimeMillis();
        System.out.println("append所消耗的時間:" + (end_03 - start_03) + "毫米");
    }
}

------------
Output:
  +   所消耗的時間:19080毫米
concat所消耗的時間:9089毫米
append所消耗的時間:10毫米

      從上面的運行結果能夠看出,append()速度最快,concat()次之,+最慢。緣由請看下面分解:

      (一)+方式拼接字符串

      在前面咱們知道編譯器對+進行了優化,它是使用StringBuilder的append()方法來進行處理的,咱們知道StringBuilder的速度比StringBuffer的速度更加快,可是爲什麼運行速度仍是那樣呢?主要是由於編譯器使用append()方法追加後要同toString()轉換成String字符串,也就說  str +=」b」等同於

       str = new StringBuilder(str).append("b").toString();

      它變慢的關鍵緣由就在於new StringBuilder()和toString(),這裏但是建立了10W個StringBuilder對象,並且每次還須要將其轉換成String,速度能不慢麼?

      (二)concat()方法拼接字符串

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    char buf[] = new char[count + otherLen];
    getChars(0, count, buf, 0);
    str.getChars(0, otherLen, buf, count);
    return new String(0, count + otherLen, buf);
    }

      這是concat()的源碼,它看上去就是一個數字拷貝形式,咱們知道數組的處理速度是很是快的,可是因爲該方法最後是這樣的:return new String(0, count + otherLen, buf);這一樣也建立了10W個字符串對象,這是它變慢的根本緣由。

     (三)append()方法拼接字符串

public synchronized StringBuffer append(String str) {
    super.append(str);
        return this;
    }

      StringBuffer的append()方法是直接使用父類AbstractStringBuilder的append()方法,該方法的源碼以下:

public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
        int len = str.length();
    if (len == 0) return this;
    int newCount = count + len;
    if (newCount > value.length)
        expandCapacity(newCount);
    str.getChars(0, len, value, count);
    count = newCount;
    return this;
    }

      與concat()方法類似,它也是進行字符數組處理的,加長,而後拷貝,可是請注意它最後是返回並無返回一個新串,而是返回自己,也就說這這個10W次的循環過程當中,它並無產生新的字符串對象。

      經過上面的分析,咱們須要在合適的場所選擇合適的字符串拼接方式,可是並不必定就要選擇append()和concat()方法,緣由在於+根據符合咱們的編程習慣,只有到了使用append()和concat()方法確實是能夠對咱們系統的效率起到比較大的幫助,纔會考慮,同時鄙人也真的沒有怎麼用過concat()方法。

 

鞏固基礎,提升技術,不懼困難,攀登高峯!!!!!!

相關文章
相關標籤/搜索