Java 中的 String、StringBuffer炮沐、StringBuilder
- 《阿里巴巴 Java 開發(fā)手冊》中有一條是關(guān)于字符串拼接的建議:
- 上面說使用 + 拼接會造成資源浪費争群,就是消耗內(nèi)存,我們就簡單模擬一下大年。
開干
- 使用 for 循環(huán)分別對String换薄、StringBuffer玉雾、StringBuilder進(jìn)行 10000 次字符串拼接,并統(tǒng)計耗時轻要。
使用 String 拼接:
static void StringTest() {
String str = "";
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str += i;
}
long end = System.currentTimeMillis();
long result = end- begin;
System.out.println("String 拼接耗時:" + result);
}
使用 StringBuffer 拼接:
static void StringBufferTest() {
StringBuffer str = new StringBuffer();
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str.append(i);
}
long end = System.currentTimeMillis();
long result = end- begin;
System.out.println("StringBuffer 拼接耗時:" + result);
}
使用 StringBilder 拼接:
static void StringBuilderTest() {
StringBuilder str = new StringBuilder();
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str.append(i);
}
long end = System.currentTimeMillis();
long result = end- begin;
System.out.println("StringBuilder 拼接耗時:" + result);
}
結(jié)果
上面的代碼复旬,同一電腦執(zhí)行結(jié)果是(單位為:Millis):
String 拼接耗時:23051
StringBuffer 拼接耗時:4
StringBuilder 拼接耗時:3
- 結(jié)果表明,三者的執(zhí)行效率是: StringBuilder > StringBuffer > String冲泥。其中 String 的耗時居然是 StringBuffer 和 StringBuilder 的幾千倍赢底。
接下來找下他們的效率快慢的原因。
分析
關(guān)于 String
String 是不可擴展的字符串變量
String 的工作方式
- 以下舉個例子:
String str = "ABCD";
str += "加點東西";
我們都知道 String 是不可擴展的字符串變量(內(nèi)存地址不會變柏蘑,創(chuàng)建了之后,該常量就不會改變了)粹庞,當(dāng)編譯器執(zhí)行 String str = "ABCD";
的時候咳焚,內(nèi)存的方法區(qū)中會分配一個空間給常量 "ABCD"
, 棧中的變量 str
指向常量 "ABCD"
的內(nèi)存地址,這時候完成 String str = "ABCD";
這行代碼庞溜。
這時候如果我們對 str
進(jìn)行拼接操作革半,即執(zhí)行 str += "加點東西";
, 那么編譯器首先會在方法區(qū)的常量池尋找有沒有 "ABCD加點東西"
這個字符串常量(這時候肯定是沒有該常量)。那么編譯器會在常量池中重新開辟一塊空間給 "ABCD加點東西"
(但是其實這個步驟流码,在底層實現(xiàn)的時候也是每次循環(huán)都會 StringBuilder對象又官,并調(diào)用其中的 append 方法進(jìn)行拼接, 這也印證了為什么會 String 比 StringBuilder 效率低),棧中的 str
變量指向 "ABCD加點東西"
的內(nèi)存地址漫试,完成一次拼接六敬。
- 這樣做就十分費內(nèi)存,也就是浪費資源驾荣。
關(guān)于 StringBuffer 和 StringBuilder
StringBuffer 和 StringBuilder 都是可擴展的字符串類型外构。
StringBuffer 和 StringBuilder 工作方式
- 舉個例子:
StringBuilder sb = new StringBuilder("ABCD");
sb.append("加點東西");
// 或者以下
StringBuffer sb = new StringBuffer("ABCD");
sb.append("加點東西");
StringBuffer 和 StringBuilder 的內(nèi)部數(shù)組 默認(rèn)長度 = 初始化字符串長度 + 16
。所以當(dāng) new StringBuilder("ABCD");
執(zhí)行完 new 之后播掷,實際上在 sb 中的 capacity() 的返回值是 20
审编。 這時候會在堆區(qū)創(chuàng)建一個 StringBuffer 或者 StringBuilder 對象
躁倒,同時會到方法去的產(chǎn)量池中尋找是否有 "ABCD"
這個常量驯绎,然后 StringBuffer 或者 StringBuilder 對象
指向常量池的 "ABCD"
這個常量。
當(dāng)執(zhí)行 sb.append("加點東西");
這行代碼的時候淑蔚,編譯器就會去常量池找是否有 "ABCD加點東西"
常量件炉,如果沒有那么就看 StringBuffer 或者 StringBuilder 對象
的長度能都裝載下 "加點東西"
該字符串(如果不夠那么就增加到當(dāng)前長度的 150%)勘究。此時的內(nèi)存地址是不變的。
- 所以相對
String
的每次拼接都要在內(nèi)存中重新分配一塊內(nèi)存空間妻率,StringBuffer 或者 StringBuilder
的效率自然而言要給String
的拼接速度要快乱顾。
StringBuffer 和 StringBuilder 區(qū)別
- 既然說
StringBuffer 和 StringBuilder
效率差不多,那么為啥還要兩個宫静?其中有什么區(qū)別呢走净?
StringBuffer
比 StringBuilder
的實現(xiàn)方法前多了一個 synchronized
券时,也就是同步鎖,這樣會使 StringBuffer
在并發(fā)編程中更加安全伏伯,可靠橘洞。StringBuilder
在日常開發(fā)中是最常見,也是效率最高的说搅。
小結(jié)
- 拼接效率:
StringBuilder > StringBuffer > String
- 推薦使用
StringBuilder
炸枣。