簡介
誰在乎toString的性能?沒有人!
除非你批量處理大量數(shù)據(jù),追求算法高性能,否則將使用toString進(jìn)行大量日常類型轉(zhuǎn)換。然后,你會研究為什么它很慢,認(rèn)識到toString()主要是使用內(nèi)部實(shí)現(xiàn)的并且可以優(yōu)化。
首先,讓我們看一下Javadoc的描述 Object.toString 應(yīng)該做什么:“ 返回對象的字符串表示形式。通常,該 toString方法返回一個(gè)“以文本形式表示”此對象的字符串。結(jié)果應(yīng)該是簡潔易懂的表示形式,便于人們閱讀。建議所有子類都重寫此方法?!啊DE(idea、eclipse)往往會為我們生成equals 、 hashcode 、 toString方法的重寫……我們通常會這樣。此外,IDE為我們提供了幾種選擇來生成toString:String級聯(lián)(使用+符號),StringBuffer,StringBuilder,ToStringBuilder,ReflectionToStringBuilder,Guava或Objects.toString ...
在這些實(shí)現(xiàn)方案中,你會選擇哪一個(gè)?
如果你想知道哪種實(shí)現(xiàn)更有效,我們可以通過JMH測試基準(zhǔn)來看看效果。
對于此基準(zhǔn)測試,我創(chuàng)建了類(使用繼承,集合等),并且使用了idea生成的所有不同的toString實(shí)現(xiàn),以查看哪個(gè)性能更高。代碼盡量簡潔,無論使用哪種技術(shù)(見下文),為一些屬性或所有屬性(包括繼承,依賴關(guān)系和集合)生成toString都會對性能產(chǎn)生巨大影響。
+符號
讓我們從性能最高的方法開始:帶+符號的字符串連接。很多人告訴我們不要使用+號來生成字符串,這種寫法不友善,尤其在JVM7之前。但是,Java Compiler會 將+符號編譯為字符串生成器(大多數(shù)情況下),做了很多的優(yōu)化。所以,不要猶豫,使用它。但是它唯一的缺點(diǎn)是不處理null值,你需要自己做特殊處理。
在以下結(jié)果中是JMH的平均性能:
public String toString() {
return "MyObject{" +
"att1='" + att1 + ''' +
", att2='" + att2 + ''' +
", att3='" + att3 + ''' +
"} " + super.toString();
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (140772,314, 142075,167, 143844,717)
Objects.toString
Java SE 7帶來了Objects類以及一些靜態(tài)方法。Objects.toString的優(yōu)點(diǎn)是它處理null值,如果為null,甚至可以設(shè)置默認(rèn)值。性能比之前的代碼略低,但是會處理null:
public String toString() {
return "MyObject{" +
"att1='" + Objects.toString(att1) + ''' +
", att2='" + Objects.toString(att2) + ''' +
", att3='" + Objects.toString(att3) + ''' +
"} " + super.toString();
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (138790,233, 140791,365, 142031,847)
StringBuilder
另一種實(shí)現(xiàn)方案是使用StringBuilder。在這里,很難分辨出哪種技術(shù)表現(xiàn)更好。后三種技術(shù)在性能方面非常相似。
public String toString() {
final StringBuilder sb = new StringBuilder("MyObject{");
sb.append("att1='").append(att1).append(''');
sb.append(", att2='").append(att2).append(''');
sb.append(", att3='").append(att3).append(''');
sb.append(super.toString());
return sb.toString();
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (96073,645, 141463,438, 146205,910)
Guava
Guava幾乎沒有幫助器類:其中之一可以幫助您生成toString。它的性能不如純JDK API,但guava可以為你提供一些額外的服務(wù)
public String toString() {
return Objects.toStringHelper(this)
.add("att1", att1)
.add("att2", att2)
.add("att3", att3)
.add("super", super.toString()).toString();
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (97049,043, 110111,808, 114878,137)
Commons Lang3
Commons Lang3有幾種生成toString的技術(shù):從生成器到內(nèi)部檢查器。如你所看的結(jié)果,內(nèi)部更易于使用,代碼行更少,但會對性能造成嚴(yán)重影響:
public String toString() {
return new ToStringBuilder(this)
.append("att1", att1)
.append("att2", att2)
.append("att3", att3)
.append("super", super.toString()).toString();
}
// Average performance with JMH (ops/s)
// (min, avg, max) = ( 73510,509, 75165,552, 76406,370)
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (31803,224, 34930,630, 35581,488)
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
// Average performance with JMH (ops/s)
// (min, avg, max) = (14172,485, 23204,479, 30754,901)
結(jié)論
如今,隨著JVM的優(yōu)化, 我們可以安全地使用+符號來連接字符串(并使用Objects.toString來處理空值)。 使用JDK內(nèi)置的實(shí)用程序類 Objects,無需外部框架即可處理空值。因此,開箱即用的JDK具有比本文介紹的任何其他技術(shù)更好的性能(如果你有其他框架/技術(shù),請留言給我,我會嘗試一下,歡迎交流)。
總結(jié)一下,這是一張表,其中包含JMH的平均表現(xiàn) (從表現(xiàn)最好的到表現(xiàn)欠佳的):
202209222331368601.png
JMH結(jié)果
同樣,如果你經(jīng)常調(diào)用toString方法,那么所有這些都很重要。如果沒有,性能并不是真正的問題,用那個(gè)都可以,怎么方便怎么來。
拓展
針對+號拼接
package tostring;
public class Main {
public static void main(String[] args) {
int n = 1000, iterations = 10000;
long len, t0, t1;
// string builder: < 1 second
len = 0;
t0 = System.currentTimeMillis();
for (int j = 0; j < iterations; j++) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < n; i++) {
builder.append(i);
}
len += builder.toString().length();
}
t1 = System.currentTimeMillis();
System.out.println(len + " " + (t1 - t0));
// string concatenation: 10 seconds
len = 0;
t0 = System.currentTimeMillis();
for (int j = 0; j < iterations; j++) {
String res = "";
for (int i = 0; i < n; i++) {
res += i;
}
len += res.length();
}
t1 = System.currentTimeMillis();
System.out.println(len + " " + (t1 - t0));
}
}
請注意字符串連接,因?yàn)镴VM不夠聰明,無法優(yōu)化復(fù)雜的流。一個(gè)簡單的循環(huán)會使性能受到很大的影響,這也就是為什么JDK強(qiáng)調(diào)“簡潔”非常重要。你應(yīng)該避免循環(huán)使用toString方法。
+的String concat與String builder有可能有同樣的性能
奇怪的是,帶有+的String concat與String builder花費(fèi)幾乎相同的時(shí)間
這個(gè)其中的原因就是編譯器做了一些優(yōu)化產(chǎn)生的,編譯時(shí),javac用StringBuilder替換串聯(lián)。
審核編輯:湯梓紅
-
JAVA
+關(guān)注
關(guān)注
20文章
2997瀏覽量
115692 -
字符串
+關(guān)注
關(guān)注
1文章
594瀏覽量
23044 -
string
+關(guān)注
關(guān)注
0文章
40瀏覽量
5018
原文標(biāo)題:+的String concat與String builder有可能有同樣的性能
文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
Linux下AWTK與Qt的性能對比
工頻機(jī)和高頻機(jī)的性能對比分析哪個(gè)好?
常用無線收發(fā)芯片性能對比分析哪個(gè)好?
談?wù)凷T的單片機(jī)分類及性能對比
arduino和stm32性能對比究竟誰更厲害?
高頻型直流充電機(jī)性能對比檢驗(yàn)試驗(yàn)總結(jié)報(bào)告

幾種toString的性能對比
評論