String、StringBuffer與StringBulider之間區別

java中String、StringBuffer、StringBulider是經常要用到的字符串類,那麼它們之間到底有什麼不同?以及它們的應用場景?

String字符串常量

String 簡介

String 類是不可變類,所以String本身的值是不能改變的,任何對String的操作都會引起新的String對象的產生.去看看String類的具體實現你就會發現,用來保存字符串的數組用final修飾:
下面的代碼段是String類的部分實現.

   private final char value[];  //保存字符串的數組.

如何證明String對象本身的值不可變?

String s="abcd";
s=s+1;
System.out.println(s);  //result:abcd1;

這段代碼大家一定已經用爛了,從結果來看,我們明明改變了String類型的變量s,爲什麼說String對象是不可變的呢,其實這是一種欺騙,JVM是這樣解析這段代碼的:首先創建對象s,賦予abcd,然後再創建一個新的對象s來執行第二行的代碼,也就是說我們之前的對象s並沒有改變.所以說String類型是不可變的對象了,由於這種機制,每當用String操作字符串時,實際上是不斷的創建新的對象,所以效率會比較高,原來的對象會被GC回收掉.看下面這段代碼:

String a="aaaaaa";  
System.out.println(a.replace('a','b'); //result:bbbbbb  
System.out.println(a);    //result:aaaaaa

從結果可以看出,執行a.repalce(‘a’,’b’);並沒有在a對象上操作,而是創建了一個新的對象. 因爲最後a的值還是aaaaaa.

StringBuffer(字符串變量)

StringBuffer簡介

操作StringBuffer類型字符串時,每次結果都會對StringBuffer對象本身進行操作,而不是生成新的對象,而改變對象引用,所以一般情況推薦使用StringBuffer,特別是字符串對象經常改變的情況下.

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value; //保存字符串的字符數組.

上面是StringBuffer的部分實現,大家可以看到,它繼承了AbstractStringBuilder,用可變的數組保存要操作的字符串.

怎樣證明StringBuffer類型的字符串是在自身上操作?

StringBuffer b=new StringBuffer("aaaaaa");  
System.out.println(b.replace(0,6,"bbbbbb");   //result:bbbbbb
System.out.println(b); result:bbbbbb; 

從結果可以看出,執行b.replace(0,6,”bbbbbb”,b的內容也變成了bbbbbb,說明,字符串的替換是在b對象本身上進行的.

線程安全的StringBuffer

StringBuffer 對方法加了同步鎖,或對調用的方法加了同步鎖,所以是線程安全的.部分源碼如下:

@Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
         }

StringBuilder(字符串變量) 

StringBuilder簡介

操作StringBuilder類型的字符串時,每次結果也都是在對象本身進行操作,不會產生新的對象.部分源碼如下:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
    abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;保存字符串的字符數組.

從上面的可以看出.它繼承了AbstractStringBuilder,用可變的數組保存要操作的字符串.

如何證明StringBulider是在對象本身進行操作?

StringBuilder b=new StringBuffer("aaaaaa");  
System.out.println(b.replace(0,6,"bbbbbb");   //result:bbbbbb

從結果可以看出,執行b.replace(0,6,”bbbbbb”,b的內容也變成了bbbbbb,說明,字符串的替換是在b對象本身上進行的.

非線程安全StringBuilder

部分源碼如下:

 @Override
    public int length() {
        return count;
    }
public AbstractStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的。

StringBuildler與StringBuffer共同點 

  • StringBuilder與StringBuffer有公共父類AbstractStringBuilder(抽象類)。
  • 抽象類與接口的其中一個區別是:抽象類中可以定義一些子類的公共方法,子類只需要增加新的功能,不需要重複寫已經存在的方法;而接口中只是對方法的申明和常量的定義。
  • StringBuilder、StringBuffer的方法都會調用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer會在方法上加synchronized關鍵字,進行同步。

String,StringBuffer,StringBulider效率問題

  • 對String類型的字符串操作每次都要構造新的字符串,效率最慢(用一種情況例外).
  • StringBuffer,StringBuilder類型的字符串的操作都在自身上操作,減少了系統創建對象的開銷,但是Stringbuffer是線程安全的,在併發操作中,對此類型的字符串的操作都是安全的,所以,相對於StringBuilder,由於線程安全要進行加鎖—>修改—->釋放鎖的邏輯,效率比較低. 所以在單線程下優先選擇StringBulider.
  • 速度從慢到快:String

應用場景

  • String 一般用於操作少量的數據.
  • 單線程操作字符串緩衝區下操作大量數據StringBuilder;
  • 多線程操作字符串緩衝區下操作大量數據StringBuffer;

測試實例

下面是一個String,StringBuffer,StringBuilder速度測試代碼: 
方法是分別用String,StringBuffer,StringBulider連接2000000個字符串,看其執行時間,由於String特別慢,而StringBulider特別快,所以String連接了20000個,而只有在大量連接操作中,StringBuilder和StringBuffer的效率草擬體現出來,所以StringBuilder和StringBuffer都連接的是2000000個字符串. 最後測試了StringBuffer和StringBulider的賦值操作:
代碼如下:

package thread;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by yang on 16-7-10.
 */

public class StringTest {

    public static String BASEINFO = "yang";
    public static final int COUNT = 2000000;

    /**
     * 執行一項String賦值測試
     */
    public static void doStringTest() {

        String str = new String(BASEINFO);
        long starttime = System.currentTimeMillis();
        for (int i = 0; i < COUNT/100; i++) {
            str = str + "xiyou";
        }
        long endtime = System.currentTimeMillis();
        System.out.println((endtime - starttime)
                + " millis has costed when used String.");
    }

    /**
     * 執行一項StringBuffer賦值測試
     */
    public static void doStringBufferTest() {

        StringBuffer sb = new StringBuffer(BASEINFO);
        long starttime = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
            sb = sb.append("miss");
        }
        long endtime = System.currentTimeMillis();
        System.out.println((endtime - starttime)
                + " millis has costed when used StringBuffer.");
    }

    /**
     * 執行一項StringBuilder賦值測試
     */
    public static void doStringBuilderTest() {

        StringBuilder sb = new StringBuilder(BASEINFO);
        long starttime = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
            sb = sb.append("miss");
        }
        long endtime = System.currentTimeMillis();
        System.out.println((endtime - starttime)
                + " millis has costed when used StringBuilder.");
    }

    /**
     * 測試StringBuffer遍歷賦值結果
     *
     * @param mlist
     */
    public static void doStringBufferListTest(List<String> mlist) {
        StringBuffer sb = new StringBuffer();
        long starttime = System.currentTimeMillis();
        for (String string : mlist) {
            sb.append(string);
        }
        long endtime = System.currentTimeMillis();
        System.out.println(sb.toString() + "buffer cost:"
                + (endtime - starttime) + " millis");
    }

    /**
     * 測試StringBuilder迭代賦值結果
     *
     * @param mlist
     */
    public static void doStringBuilderListTest(List<String> mlist) {
        StringBuilder sb = new StringBuilder();
        long starttime = System.currentTimeMillis();
        for (Iterator<String> iterator = mlist.iterator(); iterator.hasNext();) {
            sb.append(iterator.next());
        }

        long endtime = System.currentTimeMillis();
        System.out.println(sb.toString() + "builder cost:"
                + (endtime - starttime) + " millis");
    }

    public static void main(String[] args) {
        doStringTest();
        doStringBufferTest();
        doStringBuilderTest();

        List<String> list = new ArrayList<String>();
        list.add(" I ");
        list.add(" like ");
        list.add(" xiyou ");
        list.add(" linux ");
        list.add(" xingqu ");
        list.add(" xiaozu ");
        list.add(" . ");

        doStringBufferListTest(list);
        doStringBuilderListTest(list);
    }

}

結果如下:

1779 millis has costed when used String. //連接了20000個字符串
79 millis has costed when used StringBuffer. //連接了2000000個字符串.
53 millis has costed when used StringBuilder. //連接了2000000個字符串.
I like xiyou linux xingqu xiaozu. buffer cost:1 millis //7次循環賦值操作
I like xiyou linux xingqu xiaozu. builder cost:0 millis//7次循環賦值操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章