1. 程式人生 > >四:Java之字符串操作String、StringBuffer和StringBuilder

四:Java之字符串操作String、StringBuffer和StringBuilder

equal const wstring str asi 有時 string對象 階段 stringbu

string是我們經經常使用到的一個類型,事實上有時候認為敲代碼就是在重復的操作字符串,這是C的特點,在java中。jdk非常好的封裝了關於字符串的操作。三個類String 、StringBuffer 、 StringBuilder .這三個類基本上滿足了我們在不同情景下使用字符串的需求。

一、String

JDK的解釋是 “Strings are constant; their valuescannot be changed after they are created”也就是說String對象一旦被創建就是固定不變的了,這種一點優點就是能夠多線程之間訪問,由於僅僅讀不寫。

普通情況下我們以以下兩種方式創建一個String對象

Stringstr1 = “Liangcs”;

Stringstr2 = new String(“Laingcs”);

兩種方式是有差別的,這和java的內存管理有關,前面已經說過,string創建之後是不可變的。所以依照第一種方式創建的字符串會放在棧裏。更確切的是常量池中,常量池就是用來保存在編譯階段確定好了大小的數據,一般我們定義的int等基本數據類型就保存在這裏。

其詳細的一個流程就是,編譯器首先檢查常量池,看看有沒有一個“string”,假設沒有則創建。

假設有的話,則則直接把str1指向那個位置。

另外一種創建字符串的方法是通過newkeyword,還是java的內存分配,java會將new的對象放在堆中,這一部分對象是在執行時創建的對象。所以我們每一次new的時候,都會創建不同的對象,即便是堆中已經有了一個一模一樣的。

寫一個小樣例

<span style="font-size:18px;">String str1 = "string";
String str4 = "string";
String str2 = newString("string");
String str3 = newString("string");
       
/*用於測試兩種創建字符串方式的差別*/
System.out.println(str1 == str4);
System.out.println(str2 == str3);
System.out.println(str3 == str1);
       
str3 =str3.intern(); //一個不常見的方法
System.out.println(str3 == str1);
這個的執行結果是
true    //解釋:兩個字符串的內容全然同樣,因而指向常量池中的同一個區域
false   //解釋:每一次new都會創建一個新的對象
false  // 解釋: 註意==比較的是地址,不不過內容 
true //介紹一下intern方法,這種方法會返回一個字符串在常量池中的一個地址,假設常量池中有與str3內容同樣的string則返回那個地址。假設沒有,則在常量池中       創建一個string後再返回。實際上,str3如今指向了str1的地址。</span>

非常多人有這種疑問就是既然string是不變的,那麽為什麽str1 + "some"是合法的,事實上。每次對string進行改動,都會創建一個新的對象。

所以假設須要對一個字符串不斷的改動的話,效率是非常的低的,由於堆的優點是能夠動態的添加空間,劣勢就是分配新的空間消耗是非常大的。比方我們看以下的測試。

<span style="font-size:18px;">        long start =System.currentTimeMillis();
       
        for(int i = 0; i < 50000; i++)
        {
            str1+= " ";
        }    
        long end = System.currentTimeMillis();
        System.out.println("the run timeis "+(end -start)+" ms");</span>

上執行結果是the run time is 3538 ms 假設你把循環的次數後面再添加幾個0就會更慢。

由於每一次循環都在創建心的對象。那麽JDK怎樣解決問題?

以下就要說

二、StringBuffer。

StringBuffer是一個線程安全的,就是多線程訪問的可靠保證。最重要的是他是可變的,也就是說我們要操作一個常常變化的字符串,能夠使用這個類,主要的方法就是append(與string的concat方法相應)和insert方法,至於怎麽使用,就不多講了。大家能夠自己查看API。

<span style="font-size:18px;">       StringBuilder sb = new StringBuilder("string builder");
        StringBuffer sf = newStringBuffer("string buffer");
        long start =System.currentTimeMillis();
        for(int i = 0; i < 50000; i++)
        {
            //str1+= " ";
            sb.append(" ");
        }
        long end = System.currentTimeMillis();
        System.out.println("the run timeis "+(end -start)+" ms");</span>

測試一下,這次僅僅須要8ms。這就是效率。

三、StringBuilder

那麽接下來,就要問StringBuilder是幹什麽的。事實上這個才是我們嘗使用的。這個就是在jdk 1.5版本號後面加入的新的類。前面說StringBuffer是線程同步的。那麽非常多情況下。我們僅僅是使用一個線程,那個同步勢必帶來一個效率的問題,StringBuilder就是StringBuffer的非線程同步的版本號,二者的方法幾乎相同。僅僅是一個線程安全(適用於多線程)一個沒有線程安全(適用於單線程)。

事實上看了一下jdk源碼就會發現,StringBuffer就是在各個方法上加上了keywordsyncronized

StringBuilder也是一個可變的字符串對象。他與StringBuffer不同之處就在於它是線程不安全的,基於這點,它的速度一般都比StringBuffer快。與StringBuffer一樣,StringBuider的主要操作也是append與insert方法。

這兩個方法都能有效地將給定的數據轉換成字符串。然後將該字符串的字符加入或插入到字符串生成器中。

三者比較

簡要的說。 String 類型和 StringBuffer 類型的主要性能差別事實上在於 String 是不可變的對象(為什麽?問問 Java 的設計者吧,為什麽 String 不是原生類型呢?)因此在每次對 String 類型進行改變的時候事實上都等同於生成了一個新的 String 對象,然後將指針指向新的 String 對象,所以常常改變內容的字符串最好不要用 String ,由於每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以後, JVM 的 GC 就會開始工作。那速度是一定會相當慢的。這裏嘗試舉個不是非常恰當的樣例:

<span style="font-size:18px;">       String S1 = “abc”;
       For(int I = 0 ; I < 10000 ; I++)  // For 模擬程序的多次調用
       {
              S1+ = “def”;
              S1= “abc”;
       }</span>

假設是這種話,到這個 for 循環完成後,假設內存中的對象沒有被 GC 清理掉的話。內存中一共同擁有 上 萬個了。驚人的數目,而假設這是一個非常多人使用的系統,這種數目就不算非常多了。所以大家使用的時候一定要小心。

而假設是使用 StringBuffer 類則結果就不一樣了。每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象。再改變對象引用。

所以在普通情況下我們推薦使用StringBuffer ,特別是字符串對象常常改變的情況下。而在某些特別情況下, String 對象的字符串拼接事實上是被 JVM 解釋成了 StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是下面的字符串對象生成中。 String 效率是遠要比 StringBuffer 快的:

String S1 = “This is only a” + “simple” + “ test”;

StringBuffer Sb = newStringBuilder(“This is only a”).append(“ simple”).append(“ test”);

你會非常吃驚的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 竟然速度上根本一點都不占優勢。事實上這是 JVM 的一個把戲,在 JVM 眼裏。這個

String S1 = “This is only a” + “simple” + “test”; 事實上就是:

String S1 = “This is only asimple test”; 所以當然不須要太多的時間了。但大家這裏要註意的是,假設你的字符串是來自另外的 String 對象的話,速度就沒那麽快了。譬如:

String S2 = “This is only a”;

String S3 = “ simple”;

String S4 = “ test”;

String S1 = S2 +S3 + S4;

這時候 JVM 會規規矩矩的依照原來的方式去做。 S1 對象的生成速度就不像剛才那麽快了,一會兒我們能夠來個測試作個驗證。

由此我們得到第一步結論:

在大部分情況下 StringBuffer >String

而 StringBuilder 跟他們比又怎麽樣呢?先簡介一下, StringBuilder 是 JDK5.0 中新添加的一個類。它跟 StringBuffer 的差別看以下的介紹(來源 JavaWorld ):

Java.lang.StringBuffer 線程安全的可變字符序列。類似於 String 的字符串緩沖區。但不能改動。可將字符串緩沖區安全地用於多個線程。

能夠在必要時對這些方法進行同步。因此隨意特定實例上的全部操作就好像是以串行順序發生的,該順序與所涉及的每一個線程進行的方法調用順序一致。

每一個字符串緩沖區都有一定的容量。

僅僅要字符串緩沖區所包括的字符序列的長度沒有超出此容量,就無需分配新的內部緩沖區數組。

假設內部緩沖區溢出。則此容量自己主動增大。從 JDK 5.0 開始,為該類增添了一個單個線程使用的等價類。即 StringBuilder 。

與該類相比,通常應該優先使用 StringBuilder 類,由於它支持全部同樣的操作,但由於它不運行同步。所以速度更快。

可是假設將 StringBuilder 的實例用於多個線程是不安全的。須要這種同步,則建議使用 StringBuffer 。

這樣說預計大家都能明確他們之間的差別了。那麽以下我們再做一個一般性推導:

在大部分情況下 StringBuilder >StringBuffer

因此,依據這個不等式的傳遞定理: 在大部分情況下

StringBuilder > StringBuffer> String

對於三者使用的總結:
1.假設要操作少量的數據用 String
2.單線程操作字符串緩沖區 下操作大量數據 StringBuilder
3.多線程操作字符串緩沖區 下操作大量數據 StringBuffer

四、經常使用串操作

1、字符串比較

equals() ------推斷內容是否同樣。

compareTo() ------推斷字符串的大小關系。

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

== ------推斷內容與地址是否同樣。

equalsIgnoreCase() ------忽略大寫和小寫的情況下推斷內容是否同樣。

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

2、字符串查找

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

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

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

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

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

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

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

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

3、字符串截取

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

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

4、字符串替換

public String replace(char oldChar。char newChar)。

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

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

通過我自己的學習,我感覺事實上最好的資料就是JDK的API,能夠好好利用。

四:Java之字符串操作String、StringBuffer和StringBuilder