5種字符串拼接方式的效率比拼,你用對(duì)了嗎?
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
字符串拼接一般使用“+”,但是“+”不能滿足大批量數(shù)據(jù)的處理,Java中有以下五種方法處理字符串拼接,各有優(yōu)缺點(diǎn),程序開(kāi)發(fā)應(yīng)選擇合適的方法實(shí)現(xiàn)。
加號(hào) “+” String contact() 方法 StringUtils.join() 方法 StringBuffer append() 方法 StringBuilder append() 方法
經(jīng)過(guò)簡(jiǎn)單的程序測(cè)試,從執(zhí)行100次到90萬(wàn)次的時(shí)間開(kāi)銷如下表:

由此可以看出:
方法1 加號(hào) “+” 拼接 和 方法2 String contact() 方法 適用于小數(shù)據(jù)量的操作,代碼簡(jiǎn)潔方便,加號(hào)“+” 更符合我們的編碼和閱讀習(xí)慣;
方法3 StringUtils.join() 方法 適用于將ArrayList轉(zhuǎn)換成字符串,就算90萬(wàn)條數(shù)據(jù)也只需68ms,可以省掉循環(huán)讀取ArrayList的代碼;
方法4 StringBuffer append() 方法 和 方法5 StringBuilder append() 方法 其實(shí)他們的本質(zhì)是一樣的,都是繼承自AbstractStringBuilder,效率最高,大批量的數(shù)據(jù)處理最好選擇這兩種方法。
方法1 加號(hào) “+” 拼接 和 方法2 String contact() 方法 的時(shí)間和空間成本都很高(分析在本文末尾),不能用來(lái)做批量數(shù)據(jù)的處理。
源代碼,供參考
package?cnblogs.twzheng.lab2;
/**
?*?@author?Tan?Wenzheng
?*
?*/
import?java.util.ArrayList;
import?java.util.List;
import?org.apache.commons.lang3.StringUtils;
public?class?TestString?{
????private?static?final?int?max?=?100;
????public?void?testPlus()?{
????????System.out.println(">>>?testPlus()?<<<");
????????String?str?=?"";
????????long?start?=?System.currentTimeMillis();
????????for?(int?i?=?0;?i?????????????str?=?str?+?"a";
????????}
????????long?end?=?System.currentTimeMillis();
????????long?cost?=?end?-?start;
????????System.out.println("???{str?+?\"a\"}?cost="?+?cost?+?"?ms");
????}
????public?void?testConcat()?{
????????System.out.println(">>>?testConcat()?<<<");
????????String?str?=?"";
????????long?start?=?System.currentTimeMillis();
????????for?(int?i?=?0;?i?????????????str?=?str.concat("a");
????????}
????????long?end?=?System.currentTimeMillis();
????????long?cost?=?end?-?start;
????????System.out.println("???{str.concat(\"a\")}?cost="?+?cost?+?"?ms");
????}
????public?void?testJoin()?{
????????System.out.println(">>>?testJoin()?<<<");
????????long?start?=?System.currentTimeMillis();
????????List?list?=?new?ArrayList();
????????for?(int?i?=?0;?i?????????????list.add("a");
????????}
????????long?end1?=?System.currentTimeMillis();
????????long?cost1?=?end1?-?start;
????????StringUtils.join(list,?"");
????????long?end?=?System.currentTimeMillis();
????????long?cost?=?end?-?end1;
????????System.out.println("???{list.add(\"a\")}?cost1="?+?cost1?+?"?ms");
????????System.out.println("???{StringUtils.join(list,?\"\")}?cost="?+?cost
????????????????+?"?ms");
????}
????public?void?testStringBuffer()?{
????????System.out.println(">>>?testStringBuffer()?<<<");
????????long?start?=?System.currentTimeMillis();
????????StringBuffer?strBuffer?=?new?StringBuffer();
????????for?(int?i?=?0;?i?????????????strBuffer.append("a");
????????}
????????strBuffer.toString();
????????long?end?=?System.currentTimeMillis();
????????long?cost?=?end?-?start;
????????System.out.println("???{strBuffer.append(\"a\")}?cost="?+?cost?+?"?ms");
????}
????public?void?testStringBuilder()?{
????????System.out.println(">>>?testStringBuilder()?<<<");
????????long?start?=?System.currentTimeMillis();
????????StringBuilder?strBuilder?=?new?StringBuilder();
????????for?(int?i?=?0;?i?????????????strBuilder.append("a");
????????}
????????strBuilder.toString();
????????long?end?=?System.currentTimeMillis();
????????long?cost?=?end?-?start;
????????System.out
????????????????.println("???{strBuilder.append(\"a\")}?cost="?+?cost?+?"?ms");
????}
}
測(cè)試結(jié)果:
執(zhí)行100次, private static final int max = 100;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=0?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=0?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=0?ms
???{StringUtils.join(list,?"")}?cost=20?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=0?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=0?ms
執(zhí)行1000次, private static final int max = 1000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=10?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=0?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=0?ms
???{StringUtils.join(list,?"")}?cost=20?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=0?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=0?ms
執(zhí)行1萬(wàn)次, private static final int max = 10000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=150?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=70?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=0?ms
???{StringUtils.join(list,?"")}?cost=30?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=0?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=0?ms
執(zhí)行10萬(wàn)次, private static final int max = 100000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=4198?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=1862?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=21?ms
???{StringUtils.join(list,?"")}?cost=49?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=10?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=10?ms
執(zhí)行20萬(wàn)次, private static final int max = 200000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=17196?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=7653?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=20?ms
???{StringUtils.join(list,?"")}?cost=51?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=20?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=16?ms
執(zhí)行50萬(wàn)次, private static final int max = 500000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=124693?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=49439?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=21?ms
???{StringUtils.join(list,?"")}?cost=50?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=20?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=10?ms
執(zhí)行90萬(wàn)次, private static final int max = 900000;
>>>?testPlus()?<<<
???{str?+?"a"}?cost=456739?ms
>>>?testConcat()?<<<
???{str.concat("a")}?cost=186252?ms
>>>?testJoin()?<<<
???{list.add("a")}?cost1=20?ms
???{StringUtils.join(list,?"")}?cost=68?ms
>>>?testStringBuffer()?<<<
???{strBuffer.append("a")}?cost=30?ms
>>>?testStringBuilder()?<<<
???{strBuilder.append("a")}?cost=24?ms
查看源代碼,以及簡(jiǎn)單分析
String contact 和 StringBuffer,StringBuilder 的源代碼都可以在Java庫(kù)里找到,有空可以研究研究。
1.其實(shí)每次調(diào)用contact()方法就是一次數(shù)組的拷貝,雖然在內(nèi)存中是處理都是原子性操作,速度非常快,但是,最后的return語(yǔ)句會(huì)創(chuàng)建一個(gè)新String對(duì)象,限制了concat方法的速度。
public?String?concat(String?str)?{
????????int?otherLen?=?str.length();
????????if?(otherLen?==?0)?{
????????????return?this;
????????}
????????int?len?=?value.length;
????????char?buf[]?=?Arrays.copyOf(value,?len?+?otherLen);
????????str.getChars(buf,?len);
????????return?new?String(buf,?true);
????}
2.StringBuffer 和 StringBuilder 的append方法都繼承自AbstractStringBuilder,整個(gè)邏輯都只做字符數(shù)組的加長(zhǎng),拷貝,到最后也不會(huì)創(chuàng)建新的String對(duì)象,所以速度很快,完成拼接處理后在程序中用strBuffer.toString()來(lái)得到最終的字符串。
/**
?????*?Appends?the?specified?string?to?this?character?sequence.
?????*?
?????*?The?characters?of?the?{@code?String}?argument?are?appended,?in
?????*?order,?increasing?the?length?of?this?sequence?by?the?length?of?the
?????*?argument.?If?{@code?str}?is?{@code?null},?then?the?four
?????*?characters?{@code?"null"}?are?appended.
?????*?
?????*?Let?n?be?the?length?of?this?character?sequence?just?prior?to
?????*?execution?of?the?{@code?append}?method.?Then?the?character?at
?????*?index?k?in?the?new?character?sequence?is?equal?to?the?character
?????*?at?index?k?in?the?old?character?sequence,?if?k?is?less
?????*?than?n;?otherwise,?it?is?equal?to?the?character?at?index
?????*?k-n?in?the?argument?{@code?str}.
?????*
?????*?@param???str???a?string.
?????*?@return??a?reference?to?this?object.
?????*/
????public?AbstractStringBuilder?append(String?str)?{
????????if?(str?==?null)?str?=?"null";
????????int?len?=?str.length();
????????ensureCapacityInternal(count?+?len);
????????str.getChars(0,?len,?value,?count);
????????count?+=?len;
????????return?this;
????}
/**
?????*?This?method?has?the?same?contract?as?ensureCapacity,?but?is
?????*?never?synchronized.
?????*/
????private?void?ensureCapacityInternal(int?minimumCapacity)?{
????????//?overflow-conscious?code
????????if?(minimumCapacity?-?value.length?>?0)
????????????expandCapacity(minimumCapacity);
????}
????/**
?????*?This?implements?the?expansion?semantics?of?ensureCapacity?with?no
?????*?size?check?or?synchronization.
?????*/
????void?expandCapacity(int?minimumCapacity)?{
????????int?newCapacity?=?value.length?*?2?+?2;
????????if?(newCapacity?-?minimumCapacity?0)
????????????newCapacity?=?minimumCapacity;
????????if?(newCapacity?0)?{
????????????if?(minimumCapacity?0)?//?overflow
????????????????throw?new?OutOfMemoryError();
????????????newCapacity?=?Integer.MAX_VALUE;
????????}
????????value?=?Arrays.copyOf(value,?newCapacity);
????}
3.字符串的加號(hào)“+” 方法, 雖然編譯器對(duì)其做了優(yōu)化,使用StringBuilder的append方法進(jìn)行追加,但是每循環(huán)一次都會(huì)創(chuàng)建一個(gè)StringBuilder對(duì)象,且都會(huì)調(diào)用toString方法轉(zhuǎn)換成字符串,所以開(kāi)銷很大。
注:執(zhí)行一次字符串“+”,相當(dāng)于
str = new StringBuilder(str).append("a").toString();
4.本文開(kāi)頭的地方統(tǒng)計(jì)了時(shí)間開(kāi)銷,根據(jù)上述分析再想想空間的開(kāi)銷。常說(shuō)拿空間換時(shí)間,反過(guò)來(lái)是不是拿時(shí)間換到了空間呢,但是在這里,其實(shí)時(shí)間是消耗在了重復(fù)的不必要的工作上(生成新的對(duì)象,toString方法),所以對(duì)大批量數(shù)據(jù)做處理時(shí),加號(hào)“+” 和 contact 方法絕對(duì)不能用,時(shí)間和空間成本都很高。
來(lái)源:cnblogs.com/twzheng/p/5923642.html
加鋒哥微信:?java1239?? 圍觀鋒哥朋友圈,每天推送Java干貨!

