Java字符串拼接的優(yōu)雅方式實(shí)例詳解
背景
字符串拼接不管是在業(yè)務(wù)上,還是寫(xiě)算法時(shí)都會(huì)頻繁使用到。對(duì)于Java來(lái)說(shuō),字符串拼接有著很多種方式,他們之間的區(qū)別是什么,對(duì)應(yīng)不同的業(yè)務(wù)哪種更好用呢。
String底層原理
在討論字符串拼接時(shí),首先需要知道String的底層原理。
我們這里只討論jdk1.8之后的情況,看下結(jié)構(gòu)
private final byte[] value;
這一行代碼已經(jīng)可以說(shuō)明很多東西。字符串實(shí)質(zhì)就是不可變的byte數(shù)組。因?yàn)椴豢勺?,所以?duì)他進(jìn)行拼接對(duì)他拼接實(shí)際就是生成了多個(gè)對(duì)象,這就是不鼓勵(lì)對(duì)字符串進(jìn)行拼接的原因。但不可變也有很多好處,例如線(xiàn)程安全、可以存在字符串緩沖池復(fù)用字符串等。
拼接的方法
經(jīng)典但有時(shí)不優(yōu)雅的 +
String a = "123"; String b = "456"; String c = a + b;
c這個(gè)字符串就是ab拼接起來(lái)的字符串,“123456”
這段代碼反編譯出來(lái)的代碼是
String c = (new StringBuilder()).append(a).append(b).toString();
可以看出這個(gè) + 是Java的語(yǔ)法糖,他實(shí)際上是調(diào)用的StringBuilder,通過(guò)append()來(lái)進(jìn)行拼接。關(guān)于StringBuilder我們后面再講,先來(lái)講下這個(gè)用法的優(yōu)缺點(diǎn)。
優(yōu)點(diǎn)
“+”,最大的優(yōu)點(diǎn)就是簡(jiǎn)潔。如果兩個(gè)字符串需要首尾拼接,+號(hào)義不容辭的成為了最好的使用方式。
缺點(diǎn)
說(shuō)到缺點(diǎn)的話(huà)就多了。簡(jiǎn)潔也是他的最大缺點(diǎn),也就是不夠靈活。
業(yè)務(wù)一
有一個(gè)字符串List,我需要把他們拼接起來(lái),怎么辦?
for(String tmp:list){
s += tmp;
}
簡(jiǎn)潔的一批,但是他隱藏著很大的問(wèn)題!
上面說(shuō)到這種拼接方式實(shí)際是通過(guò)StringBuilder的append的方法。你不需要知道他的原理,你只需要知道,每次循環(huán),他都會(huì)new一個(gè)StringBuilder對(duì)象。創(chuàng)建對(duì)象的開(kāi)銷(xiāo)是很大的,如果List有幾千幾萬(wàn),內(nèi)存開(kāi)銷(xiāo)和時(shí)間開(kāi)銷(xiāo)是不能接受的!
所以阿里巴巴的規(guī)范說(shuō)到:

表面上是推薦,實(shí)際就是禁止。寫(xiě)算法會(huì)消耗大量時(shí)間導(dǎo)致不通過(guò),業(yè)務(wù)也會(huì)因?yàn)檫@種方式提高了無(wú)故的開(kāi)銷(xiāo),屬于領(lǐng)導(dǎo)看了想打死的代碼。
業(yè)務(wù)二
大家好,我叫XX,我是來(lái)自XXX學(xué)校的大X學(xué)生,我的愛(ài)好是XXX。
一個(gè)經(jīng)典的模板,我需要替換掉中間的XXX為controller的參數(shù),怎么辦呢?
String s = "大家好,我叫"+name+"我是來(lái)自"+school+"學(xué)校的大"+num+“學(xué)生,我的愛(ài)好是”+aihao;
屬于可用但極其丑陋的代碼。如果其他接口也需要這個(gè)模板,我還要把這段話(huà)復(fù)制到所有位置上嗎?如果我要改動(dòng)這個(gè),我要對(duì)所有代碼進(jìn)行改動(dòng)嗎。
萬(wàn)能的StringBuilder
先介紹下StringBuilder的原理。把字符串拼接想象成數(shù)組就很好理解了,StringBuilder有點(diǎn)類(lèi)似于ArrayList,可變數(shù)組。
? ?/** ? ? * The value is used for character storage. ? ? */ ? ?char[] value;
區(qū)別就是沒(méi)有final修飾,當(dāng)?shù)竭_(dá)閾值時(shí)進(jìn)行擴(kuò)容操作。append方法就是往后插入。
那么就可以解決上面業(yè)務(wù)一的問(wèn)題了。
StringBuilder sb = new StringBuilder();
for(String tmp:list){
sb.append(tmp);
}
String s = sb.tostring();
相比于上面,只創(chuàng)建了一個(gè)StringBuilder對(duì)象,減少循環(huán)創(chuàng)建的開(kāi)銷(xiāo)。
線(xiàn)程安全的StringBuffer
StringBuffer與StringBuilder相比,有線(xiàn)程安全的優(yōu)勢(shì),通過(guò)上鎖的方式。同時(shí)導(dǎo)致效率略低于StringBuilder。
靈活的String.format()
這個(gè)嚴(yán)格來(lái)說(shuō)應(yīng)該叫做格式化,但也可以用來(lái)拼接。
熟悉c語(yǔ)言的應(yīng)該能夠懂,我這里舉一個(gè)例子
String msg = String.format(“我是%s小學(xué)的學(xué)生,我愛(ài)吃%s”,"陽(yáng)光","水果"); //輸出 我是陽(yáng)光小學(xué)的學(xué)生,我愛(ài)吃水果
使用字符串鏈代替%s,生成需要的字符串。也不僅可以拼接字符串,可以看下下圖(偷的圖,沒(méi)全部驗(yàn)證過(guò),錯(cuò)了別找我)

這種方式就解決了業(yè)務(wù)二的問(wèn)題。通過(guò)編寫(xiě)枚舉或者常量字符串留出對(duì)應(yīng)的位置,使用時(shí)再用String.format()拼接。
有點(diǎn)綠色的concat
為什么說(shuō)他綠色呢,就是我還沒(méi)有找到他有什么優(yōu)勢(shì)。
String s = "123".concat("456");
//結(jié)果等價(jià)于
String s = "123" + "456";
concat方法的原理是數(shù)組擴(kuò)容后復(fù)制之前的內(nèi)容并寫(xiě)新的內(nèi)容,和StringBuilder底層有點(diǎn)相像。
但是相比于“+”號(hào)來(lái)說(shuō),既不簡(jiǎn)便,又沒(méi)有什么效率上的提高。在循環(huán)字符串拼接的條件,效率上會(huì)略有一點(diǎn)優(yōu)勢(shì),但是這種情況是根本不被允許的,所以concat就很雞肋。
JDK1.8優(yōu)雅寫(xiě)法
剛才提到業(yè)務(wù)一的解決辦法可以使用樸素的StringBuilder來(lái)解決,但是對(duì)于業(yè)務(wù)代碼來(lái)說(shuō)有一點(diǎn)冗長(zhǎng)。
Jdk1.8給出了優(yōu)雅的答案
String s = String.join("_", list);
一行代碼,就可以把list里的字符串通過(guò)“_”拼接起來(lái)。
經(jīng)典的Guava
guava是我們crud程序員的好伙伴,這里就不用多說(shuō)了。我們最常接觸到的其實(shí)就是guava的本地緩存和字符串操作。
String result = Joiner.on(",").join(list);
也是簡(jiǎn)潔的一句話(huà),但是相比于jdk本土的字符串方法來(lái)說(shuō),他還有一些其他的特性。例如可以把為null的數(shù)組給跳過(guò)或者替換掉等等。功能要比jdk的要豐富一點(diǎn)。在正常的web項(xiàng)目里基本都會(huì)有Guava的依賴(lài),使用起來(lái)還是很方便的。
總結(jié)
這篇文章偏重于代碼編寫(xiě)方面,如何寫(xiě)出簡(jiǎn)潔高效的代碼,是我們要追求的。
到此這篇關(guān)于Java字符串拼接的優(yōu)雅方式的文章就介紹到這了,更多相關(guān)Java字符串拼接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Eclipse手動(dòng)導(dǎo)入DTD文件實(shí)現(xiàn)方法解析
這篇文章主要介紹了Eclipse手動(dòng)導(dǎo)入DTD文件實(shí)現(xiàn)方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
Java 替換字符串右側(cè)出現(xiàn)的第一個(gè)子串方式
這篇文章主要介紹了Java 替換字符串右側(cè)出現(xiàn)的第一個(gè)子串方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶(hù)端
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶(hù)端,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
使用Java和PostgreSQL存儲(chǔ)向量數(shù)據(jù)的實(shí)現(xiàn)指南
在當(dāng)今的數(shù)字化時(shí)代,數(shù)據(jù)存儲(chǔ)的方式和技術(shù)正變得越來(lái)越復(fù)雜和多樣化,隨著機(jī)器學(xué)習(xí)和數(shù)據(jù)科學(xué)的發(fā)展,向量數(shù)據(jù)的存儲(chǔ)和管理變得尤為重要,本文將詳細(xì)介紹如何使用 Java 和 PostgreSQL 數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)向量數(shù)據(jù),需要的朋友可以參考下2024-09-09
Spring @Configuration和@Component的區(qū)別
今天小編就為大家分享一篇關(guān)于Spring @Configuration和@Component的區(qū)別,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
JDK動(dòng)態(tài)代理之ProxyGenerator生成代理類(lèi)的字節(jié)碼文件解析
這篇文章主要為大家詳細(xì)介紹了JDK動(dòng)態(tài)代理之ProxyGenerator生成代理類(lèi)的字節(jié)碼文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
為什么程序中突然多了 200 個(gè) Dubbo-thread 線(xiàn)程的說(shuō)明
mybatis動(dòng)態(tài)sql之Map參數(shù)的講解

