字符串的不可變性
定義一個字符串
String s = "abcd";

使用變量賦值
String s2 = s;

字符串連接
s = s.contact("ef");

可以看出,String對象一旦被創(chuàng)建出來,就無法修改。如果需要一個可修改的字符串,應(yīng)該使用StringBuffer或者StringBuilder,否則會有大量時間浪費在垃圾回收上。
編譯器對“+”的優(yōu)化
- 對于String s = "a" + "b",編譯后變成String s = "ab"。
- 對于String s = "a" + 變量,編譯后用StringBuilder.append()方法替代,最后調(diào)用toString()方法。
字符串拼接的幾種方式
- 使用“+”拼接
String str = "str"; str += String.valueOf(i);使用“+”拼接,實際上是用StringBuilder.append()。
- String.concat()
String str = "str"; str = str.concat(String.valueOf(i));String.contact實現(xiàn)的原理是,創(chuàng)建一個字符數(shù)組,長度是已有字符串和待拼接字符串的長度之和,再把兩個字符串的值復(fù)制到新的字符數(shù)組中,并使用這個字符數(shù)組創(chuàng)建一個新的String對象并返回。簡單地說就是new了一個新的String對象。
- StringBuilder.append()
StringBuilder builder = new StringBuilder("str"); builder.append(String.valueOf(i));StringBuilder.append實現(xiàn)的原理是,StringBuilder內(nèi)部也有一個char數(shù)組,但不是final的,進行append時,會直接拷貝字符到內(nèi)部的字符數(shù)組中,如果字符數(shù)組長度不夠,會進行擴展。
- StringBuffer.append()
StringBuffer buffer = new StringBuffer("str"); buffer.append(String.valueOf(i));StringBuffer.append和StringBuilder.append原理是一樣的,不同的是,StringBuffer的append方法是用synchronized修飾的,是線程安全的。
- String.join()
String str = "str"; str = String.join("", str, String.valueOf(i));String.join內(nèi)部也是用StringBuilder來實現(xiàn)的。
字符串拼接性能大比拼
測試代碼都類似下面這段:
public void stringJoint1() {
long t1 = System.currentTimeMillis();
String str = "str";
for (int i = 0; i < 50000; i ++) {
str += String.valueOf(i);
}
long t2 = System.currentTimeMillis();
System.out.println("+ cost: " + (t2 - t1));
}
運行結(jié)果:
String.concat cost: 1902
StringBuilder cost: 6
StringBuffer cost: 5
+ cost: 5202
String.join cost: 5298
多次運行之后,可以觀察到大概的運行效率是:
StringBuilder ≈ StringBuffer < String.contact < String.join ≈ "+"
- StringBuffer相對StringBuilder多了同步的操作,所以在單線程環(huán)境下運行效率差不多;
- String.contact每次都會創(chuàng)建新的字符串,所以會更慢一些;
- String.join和“+”每次都會創(chuàng)建StringBuilder對象所以更慢。
總結(jié)一下:
- 如果不在循環(huán)體中拼接字符串,直接使用+就可以。
- 如果在循環(huán)體中拼接字符串,非并發(fā)時使用StringBuilder,并發(fā)時使用StringBuffer。