京東一面:為什么 IDEA 建議去掉 StringBuilder,而要使用 “+” 拼接字符串?
今天咱們來(lái)聊聊一個(gè)很常見的開發(fā)場(chǎng)景:字符串拼接
。在日常開發(fā)中,字符串拼接幾乎是每個(gè) Java 開發(fā)者都會(huì)用到的操作。但最近,有朋友在面試時(shí)被問到一個(gè)問題:“為什么 IDEA 建議用‘+’拼接字符串,而不是用 StringBuilder?”這問題聽起來(lái)是不是有點(diǎn)反直覺?畢竟,在大家的普遍認(rèn)知中,用 StringBuilder 拼接字符串效率更高。
一、“+” 拼接字符串
先來(lái)說說“+”拼接字符串。在 Java 中,“+” 是一個(gè)非常直觀的字符串拼接操作符。比如,"Hello" + " " + "World"
,結(jié)果就是 "Hello World"
。簡(jiǎn)單、直接、易讀,這是它的優(yōu)點(diǎn)。
但長(zhǎng)期以來(lái),我們一直被告知:“+”拼接字符串效率很低,尤其是在循環(huán)中。因?yàn)槊看纹唇佣紩?huì)創(chuàng)建一個(gè)新的字符串對(duì)象,導(dǎo)致大量的臨時(shí)對(duì)象產(chǎn)生,增加了垃圾回收的負(fù)擔(dān)。所以,很多開發(fā)者會(huì)習(xí)慣性地使用 StringBuilder 來(lái)代替“+”,尤其是在處理復(fù)雜的字符串拼接時(shí)。
然而,從 JDK 5 開始,Java 編譯器做了一個(gè)優(yōu)化——當(dāng)你使用“+”拼接字符串時(shí),編譯器會(huì)自動(dòng)將其優(yōu)化為使用 StringBuilder 的方式。也就是說,"a" + "b"
在編譯后,實(shí)際上會(huì)被編譯器轉(zhuǎn)換為 new StringBuilder().append("a").append("b").toString()
。這樣一來(lái),“+”拼接字符串的性能問題就得到了解決。
二、實(shí)踐驗(yàn)證
為了驗(yàn)證這一點(diǎn),我們來(lái)做一個(gè)簡(jiǎn)單的實(shí)驗(yàn)。寫一個(gè)測(cè)試類,分別用“+”和 StringBuilder 拼接字符串,然后比較它們的性能。
public String concatenationStringByPlus(String prefix, int i) {
return prefix + "-" + i;
}
public String concatenationStringByStringBuilder(String prefix, int i) {
return new StringBuilder().append(prefix).append("-").append(i).toString();
}
然后,我們用 JUnit 測(cè)試用例分別調(diào)用這兩種方法,拼接 100000 次,看看耗時(shí)情況:
@Test
public void testStringConcatenationByPlus() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
concatenationStringByPlus("testByPlus:", i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用 '+' 拼接 100000 次,耗時(shí):" + (endTime - startTime) + " 毫秒");
}
@Test
public void testStringConcatenationByStringBuilder() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
concatenationStringByStringBuilder("testByStringBuilder:", i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用 StringBuilder 拼接 100000 次,耗時(shí):" + (endTime - startTime) + " 毫秒");
}
運(yùn)行結(jié)果:
使用 '+' 拼接 100000 次,耗時(shí):33 毫秒
使用 StringBuilder 拼接 100000 次,耗時(shí):36 毫秒
可以看到,兩者的耗時(shí)幾乎一致。這說明在普通拼接場(chǎng)景下,“+” 和 StringBuilder 的性能幾乎沒有區(qū)別。而且,“+” 的代碼更簡(jiǎn)潔、更易讀
。
三、循環(huán)拼接的“陷阱”
那么,是不是在所有場(chǎng)景下,“+” 都和 StringBuilder 一樣高效呢?答案是否定的。當(dāng)涉及到循環(huán)拼接時(shí),“+” 的效率問題就暴露出來(lái)了。
我們?cè)僮鲆粋€(gè)實(shí)驗(yàn),模擬循環(huán)拼接一個(gè)長(zhǎng)字符串。這次,我們分別用“+”和 StringBuilder 來(lái)拼接 10000 次:
@Test
public void testLoopConcatenationByPlus() {
long startTime = System.currentTimeMillis();
String str = "Initial String";
for (int i = 0; i < 10000; i++) {
str = str + "-" + i;
}
long endTime = System.currentTimeMillis();
System.out.println("使用 '+' 循環(huán)拼接 10000 次,耗時(shí):" + (endTime - startTime) + " 毫秒");
}
@Test
public void testLoopConcatenationByStringBuilder() {
long startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("Initial String");
for (int i = 0; i < 10000; i++) {
sb.append("-").append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用 StringBuilder 循環(huán)拼接 10000 次,耗時(shí):" + (endTime - startTime) + " 毫秒");
}
運(yùn)行結(jié)果:
使用 '+' 循環(huán)拼接 10000 次,耗時(shí):463 毫秒
使用 StringBuilder 循環(huán)拼接 10000 次,耗時(shí):13 毫秒
可以看到,循環(huán)拼接時(shí),“+” 的效率遠(yuǎn)遠(yuǎn)低于 StringBuilder。這是因?yàn)槊看窝h(huán)時(shí),“+” 都會(huì)創(chuàng)建一個(gè)新的 StringBuilder 對(duì)象,而 StringBuilder 只需要在同一個(gè)對(duì)象上追加內(nèi)容,效率自然更高
。
四、為什么 IDEA 建議用“+”?
既然在循環(huán)拼接中 StringBuilder 更高效,為什么 IDEA 還要建議用“+”呢?原因在于編譯器的優(yōu)化和代碼的可讀性
。
對(duì)于簡(jiǎn)單的字符串拼接,編譯器會(huì)自動(dòng)將“+”優(yōu)化為 StringBuilder 的形式。在這種情況下,使用“+”不僅代碼更簡(jiǎn)潔,而且性能也一樣好。而 StringBuilder 的代碼相對(duì)冗長(zhǎng),可讀性稍差
。
但如果是在循環(huán)中拼接字符串,IDEA 通常會(huì)提示你使用 StringBuilder,因?yàn)樗?code>明顯提升性能。
五、總結(jié)
通過以上實(shí)驗(yàn)和分析,我們可以得出以下結(jié)論:
- 普通拼接場(chǎng)景:使用“+”和使用 StringBuilder 的性能幾乎一致。由于“+”的代碼更簡(jiǎn)潔、更易讀,推薦在普通拼接場(chǎng)景下使用“+”。
- 循環(huán)拼接場(chǎng)景:推薦使用 StringBuilder。因?yàn)椤?”在循環(huán)中會(huì)不斷創(chuàng)建新的 StringBuilder 對(duì)象,導(dǎo)致性能大幅下降,而 StringBuilder 只需要初始化一次,效率更高。
圍觀朋友?:dabinjava
#java#