string拼接_String拼接操作-的优化
很多講Java優(yōu)化的文章都會強(qiáng)調(diào)對String拼接的優(yōu)化。倒不用特意記,本質(zhì)上在于對不可變類優(yōu)勢和劣勢的理解上。
需要關(guān)注的是編譯器對String拼接做出的優(yōu)化,在簡單場景下的性能能夠與StringBuilder相當(dāng),復(fù)雜場景下仍然有較大的性能問題。網(wǎng)上關(guān)于這一問題講的非常亂;如果我講的有什么紕漏,也歡迎指正。
本文用到了反編譯工具jad。在查閱網(wǎng)上關(guān)于String拼接操作的優(yōu)化時發(fā)現(xiàn)了這個工具,能同時反編譯出來源碼和字節(jié)碼,親測好用,點(diǎn)我下載。
String拼接的性能問題
優(yōu)化之前,每次用”+”拼接,都會生成一個新的String。特別在循環(huán)拼接字符串的場景下,性能損失是極其嚴(yán)重的:
所謂簡單場景
簡單場景和復(fù)雜場景是我亂起的名字,幫助理解編譯器的優(yōu)化方案。
簡單場景可理解為在一句中完成拼接:
int?i?=?0;String?sentence?=?“Hello”?+?“world”?+?String.valueOf(i)?+?“”;System.out.println(sentence);利用jad可看到優(yōu)化結(jié)果:
int?i?=?0;String?sentence?=?(new?StringBuilder()).append(“Hello”).append(“world”).append(String.valueOf(i)).append(“”).toString();System.out.println(sentence);是不是很神奇,竟然把String的拼接操作優(yōu)化成了StringBuilder#append()!
此時,可以認(rèn)為已經(jīng)將簡單場景的空間性能、時間性能優(yōu)化到最優(yōu)(僅針對String拼接操作而言),看起來編譯器已經(jīng)完成了必要的優(yōu)化。你可以測試一下,簡單場景下的性能能夠與StringBuilder相當(dāng)。但是——“但是”以前的都是廢話——編譯器的優(yōu)化對于復(fù)雜場景的幫助卻很有限了。
所謂復(fù)雜場景
所謂復(fù)雜場景,可理解為“編譯器不確定(或很難確定,于是不做分析)要進(jìn)行多少次字符串拼接后才需要轉(zhuǎn)換回String”。可能表述不準(zhǔn)確,理解個大概就好。
我們分析一個最簡單的復(fù)雜場景:
String?sentence?=?“”;for?(int?i?=?0;?i?理想的優(yōu)化方案
當(dāng)然,無論什么場景,程序猿都可以手動優(yōu)化:
- 在性能敏感的場景使用StringBuilder完成拼接。
- 在性能不敏感的場景使用更方便的String。
PS:別吐槽,這樣的API設(shè)計(jì)是合理的,在合適的地方做合適的事。
理想目標(biāo)是把這件事交給javac和JIT:
- 設(shè)定一個拼接次數(shù)的閾值,超過閾值就啟動優(yōu)化(對于javac有一個編譯期的閾值,JIT有一個運(yùn)行期的閾值,以分階段優(yōu)化)。
- 優(yōu)化時,在拼接前生成StringBuilder對象,將拼接操作換成StringBuilder#append(),繼續(xù)使用該對象,直至“需要”String對象時,使用StringBuilder#toString()“懶加載”新的String對象。
該優(yōu)化方案的難度在于代碼分析:機(jī)器很難知道到底何時“需要”String對象,所以也很難在合適的位置注入代碼完成“懶加載”。
雖然很難實(shí)現(xiàn),但還是給出理想的優(yōu)化結(jié)果,以供實(shí)際方案對比:
String?sentence?=?“”;StringBuilder?sentenceSB?=?new?StringBuilder(sentence);for?(int?i?=?0;?i?實(shí)際的優(yōu)化方案
利用jad查看實(shí)際的優(yōu)化結(jié)果:
String?sentence?=?“”;for?(int?i?=?0;?i?可以看到,實(shí)際上編譯器的優(yōu)化只能達(dá)到簡單場景的最優(yōu):僅優(yōu)化字符串拼接的一句。這種優(yōu)化程度,對于上述復(fù)雜場景的性能提升很有限,循環(huán)時還是會生成大量短命垃圾,特別是字符串拼接到很大的時候,空間和時間上都是致命的。
通過對理想方案的分析,我們也能理解編譯器優(yōu)化的無奈之處:編譯器無法(或很難)通過代碼分析判斷何時是最晚進(jìn)行懶加載的時機(jī)。為什么呢?我們將代碼換個形式可能更容易理解:
String?sentence?=?“”;for?(int?i?=?0;?i?觀察第3行的代碼,等式右側(cè)引用了sentence。我肉眼知道這句話只完成了字符串拼接,機(jī)器呢?最起碼,現(xiàn)在的機(jī)器還很難通過代碼判斷。
待以后將人工智能與編譯優(yōu)化結(jié)合起來,就算只能以90%的概率完成優(yōu)化,也是非常cool的。
總結(jié)
這個問題我沒有做性能測試。其實(shí)也沒必要過于深究,與其讓編譯器以隱晦的方式完成優(yōu)化,不如用代碼進(jìn)行主動、清晰的優(yōu)化,讓代碼能夠“自解釋”。
那么,如果需要優(yōu)化,使用StringBuilder吧。
總結(jié)
以上是生活随笔為你收集整理的string拼接_String拼接操作-的优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 套口机跳针修理带图_套口机维修注意事项
- 下一篇: mybatis返回map键值对_myba