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次循環賦值操作