几种toString的性能对比

简介
谁在乎tostring的性能?没有人!
除非你批量处理大量数据,追求算法高性能,否则将使用tostring进行大量日常类型转换。然后,你会研究为什么它很慢,认识到tostring()主要是使用内部实现的并且可以优化。
首先,让我们看一下javadoc的描述 object.tostring 应该做什么:“ 返回对象的字符串表示形式。通常,该 tostring方法返回一个“以文本形式表示”此对象的字符串。结果应该是简洁易懂的表示形式,便于人们阅读。建议所有子类都重写此方法。“。ide(idea、eclipse)往往会为我们生成equals 、 hashcode 、 tostring方法的重写……我们通常会这样。此外,ide为我们提供了几种选择来生成tostring:string级联(使用+符号),stringbuffer,stringbuilder,tostringbuilder,reflectiontostringbuilder,guava或objects.tostring ...
在这些实现方案中,你会选择哪一个?
如果你想知道哪种实现更有效,我们可以通过jmh测试基准来看看效果。
对于此基准测试,我创建了类(使用继承,集合等),并且使用了idea生成的所有不同的tostring实现,以查看哪个性能更高。代码尽量简洁,无论使用哪种技术(见下文),为一些属性或所有属性(包括继承,依赖关系和集合)生成tostring都会对性能产生巨大影响。
+符号
让我们从性能最高的方法开始:带+符号的字符串连接。很多人告诉我们不要使用+号来生成字符串,这种写法不友善,尤其在jvm7之前。但是,java compiler会 将+符号编译为字符串生成器(大多数情况下),做了很多的优化。所以,不要犹豫,使用它。但是它唯一的缺点是不处理null值,你需要自己做特殊处理。
在以下结果中是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类以及一些静态方法。objects.tostring的优点是它处理null值,如果为null,甚至可以设置默认值。性能比之前的代码略低,但是会处理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
另一种实现方案是使用stringbuilder。在这里,很难分辨出哪种技术表现更好。后三种技术在性能方面非常相似。
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可以为你提供一些额外的服务
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的技术:从生成器到内部检查器。如你所看的结果,内部更易于使用,代码行更少,但会对性能造成严重影响:
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)  
结论
如今,随着jvm的优化, 我们可以安全地使用+符号来连接字符串(并使用objects.tostring来处理空值)。 使用jdk内置的实用程序类 objects,无需外部框架即可处理空值。因此,开箱即用的jdk具有比本文介绍的任何其他技术更好的性能(如果你有其他框架/技术,请留言给我,我会尝试一下,欢迎交流)。
总结一下,这是一张表,其中包含jmh的平均表现 (从表现最好的到表现欠佳的):
202209222331368601.png
jmh结果
同样,如果你经常调用tostring方法,那么所有这些都很重要。如果没有,性能并不是真正的问题,用那个都可以,怎么方便怎么来。
拓展
针对+号拼接
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)); } }  
请注意字符串连接,因为jvm不够聪明,无法优化复杂的流。一个简单的循环会使性能受到很大的影响,这也就是为什么jdk强调“简洁”非常重要。你应该避免循环使用tostring方法。
+的string concat与string builder有可能有同样的性能
奇怪的是,带有+的string concat与string builder花费几乎相同的时间
这个其中的原因就是编译器做了一些优化产生的,编译时,javac用stringbuilder替换串联。


屏下摄像头又增一员 华为加入屏下摄像头大战
吉利控股集团与中国电信在北京举行战略合作框架协议签约仪式
紫外线光固化UV胶水的特性优点及应用领域有哪些呢?
AirPods一年卖了将近6000万 在市场中占据了主导地位
eSIM是物联网一直在等待的加速器吗
几种toString的性能对比
新一代iPad Pro几乎已经提前“预定”了本场发布会的主角位置
我国需推进高效电机设备的生产和应用
区块链如何描绘新的知识产权保护图景
6V6管制差分单端胆功放的原理及制作
网络交换机最主要的作用是什么
BOM准确率提高方法
使用Nuba扩展在Python中编写光线跟踪应用程序
家用电器专用滤波器的作用和使用方法
pcb绿油起泡原因
富士康投身工业互联网的独门秘籍是什么?
大学毕业设计一席谈之五十 删余卷积码仿真(3) 集群设备产品代码
TE Connectivity推出新型CII FC-335系列继电器 可提供强大的电源切换能力
屏下前摄即将到来,iQOO旗舰还值得购买么?
lm317可调稳压电源研制的目的和意义 LM317应用电路原理图