String,StringBuffer,StringBuilder三者的使用方法和區別
在java中,我們常常用到String型別來操作字串,但是用來操作字串變數的不僅僅只有String型別,還有StringBuilder和StringBuffer型別,儘管我們平時使用String型別比較多,但是在實際的開發中,這三種類型完全是三足鼎立的局面,根據不同的使用場景,我們要使用不同的型別,在很多情況下,使用StringBuilder和StringBuffer比使用String型別更快,還會有其它優點。
我們先來了解一下StringBuilder和StringBuffer的簡單用法,至於String的用法,我們就不再贅述了,後面拿他們三個作比較的時候我們會提到,因為StringBuffer和StringBuilder的方法基本上都一樣,所以我們共同介紹它們的方法
1.StringBuffer,StringBuilder的用法
toString()方法
將StringBuffer,StringBuilder物件轉換為String字串,常用在需要輸出的時候,因為StringBuffer和StringBuilder的物件不能直接輸出,例如:
StringBuffer s1 = new StringBuffer();
s1.toString();
append()方法
用於在字串的後面追加字串,當StringBuffer,StringBuilder中沒有字串的時候也可以append(),可以用來初始化,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
}
執行結果:
charaAt()方法
返回指定索引位置的字元,索引從0開始,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer ().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
System.out.println(s1.charAt(3));
}
結果:
deleteCharAt()方法
刪除指定索引位置的字元,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
s1.deleteCharAt(3);
System.out.println(s1.toString());
}
結果:
delete()方法
刪除從開始索引到結束索引的字串,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
s1.delete(2,4);
System.out.println(s1.toString());
}
結果:
insert()方法
在指定索引位置之前插入字串,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
s1.insert(2,"cc");
System.out.println(s1.toString());
}
結果:
indexOf()方法
返回指定字串的開始字元索引位置,還可以從某個字元索引位置開始向後匹配,沒有找到匹配的就會返回-1,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
System.out.println(s1.indexOf("ba"));
System.out.println(s1.indexOf("ba",2));
System.out.println(s1.indexOf("mn"));
}
結果:
lastIndexOf()方法
和indexOf()的用法一樣,只不過是從後往前匹配,也支援從指定索引開始從後往前去匹配,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
System.out.println(s1.lastIndexOf("b",5));
}
結果:
reverse()方法
反轉字串,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
System.out.println(s1.reverse());
System.out.println(s1.toString());
System.out.println(s1.length());
}
結果:
length()方法
返回字串的長度,例如:
public static void main(String[] args)
{
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString());
System.out.println(s1.length());
}
結果:
2.String、StringBuffer、StringBuilder的區別
重頭戲來了,前面的部分相信你即使沒有在StringBuffer/StringBuilder中用過,也在別的類中用過,那麼,這三個型別到底有什麼區別呢?怎麼選擇它們的應用場景呢?
首先,從效能、速度方面來說:
StringBuilder>StringBuffer>String
我們來做一個測試,我們分別使用String和StringBuilder建立變數,然後分別對它們進行加字串操作,由於時間太短,我們把這個過程使用for迴圈重複100000遍以放大差距:
public static void main(String[] args)
{
Long start1 = System.currentTimeMillis();//獲取開始時間
for (int i=0;i<100000;i++)//重複10萬次進行String變數加操作
{
String str = "a";
str+="b";
}
Long end1 = System.currentTimeMillis();//獲取結束時間
System.out.println("String花費時間:"+(end1-start1));//打印出花費的時間
Long start2 = System.currentTimeMillis();
for (int i=0;i<100000;i++)//重複10萬次進行StringBuilder變數加操作
{
StringBuilder str2 = new StringBuilder("a");
str2.append("b");
}
Long end2 = System.currentTimeMillis();
System.out.println("StringBuilder花費時間:"+(end2-start2));
Long start3 = System.currentTimeMillis();
for (int i=0;i<100000;i++)//重複10萬次進行StringBuffer變數加操作
{
StringBuffer str2 = new StringBuffer("a");
str2.append("b");
}
Long end3 = System.currentTimeMillis();
System.out.println("StringBuffer花費時間:"+(end2-start2));
}
實驗結果:
可以看到,放大了差距之後,String和StringBuilder、StringBuffer兩兄弟的差距還是蠻大的,那麼是什麼造成了這種差距呢?
在上面的程式中:
String str = "a";
str+="b";
這句話看似是對同一個String型別的str物件進行了加操作,但是實際上可不是同一個物件,事實上,我們先聲明瞭一個String型別的物件,值是"a",把str這個控制代碼指向了這個物件,然後,當我們把這個物件進行+=操作的時候,實際上是又建立了一個String物件,這個物件的值是"a"+“b"也就是"ab”,然後改變控制代碼str讓它指向了這個新的物件,原來的物件失去了引用,就被jvm垃圾回收了。而StringBuffer和StringBuilder可不是這樣,這兩兄弟是直接改變自己本身物件的值。
那麼,當我們進行了10萬次操作的時候,快慢差距自然就體現出來了。
這裡有人會問了,如果我把這句程式碼:
String str = "a";
str+="b";
改為:
String str = "a"+"b";
呢?
讓我們看一下執行效果:
哪怕是進行10萬次操作,String所花費的時間也是極少的,這是為什麼呢?
這是因為String和我們其它型別的變數不同,其它的非基本型別物件的值、資料都儲存在java的堆中,而String型別的變數的值是儲存jvm在方法區中的字串常量池中的。當我們執行:String str = “a”+“b”;這句話的時候,String會自動把這個物件的值看成"ab",然後在方法區中如果找到了值同樣為"ab"的,就會直接讓str控制代碼指向它,也就是說,我們的這句程式碼現在相當於:
String str = "ab";
這可比之前的String用法一遍遍反覆地去建立物件回收物件快多了,因此,即使重複10萬次依然還是很快。
從執行緒安全的角度去看
StringBuffer是執行緒安全的,而StringBuilder是執行緒不安全的,實驗的具體方法見此連結:https://blog.csdn.net/litterfrog/article/details/76862435
總結
String適用於少量的字串操作的情況
StringBuilder適用於單執行緒下在字元緩衝區進行大量操作的情況
StringBuffer適用多執行緒下在字元緩衝區進行大量操作的情況