谁关心toString的性能?没有人!除非在批处理中有大量数据,否则使用toString会产生大量日志。然后,您调查为什么它这么慢,并意识到大多数toString方法都使用自省,这实际上可以进行优化。但首先,让我们看看Javadoc回忆Object.toString应该做什么:“返回此对象的字符串表示形式。结果必须简洁但信息丰富。建议所有子类重写此方法。”这里最有趣的是“简洁”和“详细”。我们最喜欢的IDE通常会为我们生成equals/hashcode/toString方法,而我们通常不会管它们。此外,这些IDE提供了多种方式来生成我们自己的toString:字符串连接(使用+号)、StringBuffer、StringBuilder、ToStringBuilder(CommonsLang3)、ReflectionToStringBuilder(CommonsLang3)、Guava或Objects.toString...我应该选择一个吗?如果你想知道toString的哪个实现会更有效率,不要猜测,测试吧!这时候就需要用到JMH了。我之前已经写过博客,所以我不会在这里详细介绍JMH的工作原理。在这个基准测试中,我创建了一个复杂的对象图(使用继承、集合等),我使用了IDE生成的所有不同的toString实现来查看哪个执行得更好。只有一个经验法则:简洁。无论您使用哪种技术(见下文),为部分或所有属性(包括继承、依赖项或集合)生成toStings都会对性能产生巨大影响。用+连接字符串让我们从最有效的方法开始:用+连接字符串。曾经被认为是邪恶的用法(“不要用+!!!连接字符串”)变得很酷而且很高效!现在JVM编译器(大部分时间)会将+编译成一个字符串生成器。所以,请毫不犹豫地使用它。唯一不足的是没有处理空值,需要自己处理。在下面的评论中使用JMH统计数据查看平均性能。publicStringtoString(){return"MyObject{"+"att1='"+att1+'''+",att2='"+att2+'''+",att3='"+att3+'''+"}"+super.toString();}//使用JMH的平均性能(ops/s)//(min,avg,max)=(140772,314,142075,167,143844,717)//使用JMH来measureOutoftheaverageperformance//(minimum,average,***)=(140772,314,142075,167,143844,717)concatenatingstringswithObjects.toStringJavaSE7引入了Objects类及其一些静态方法.Objects.toString的优点是可以处理null值,甚至可以为null设置一个默认值。它的性能略低于前一个,但是可以处理空值:publicStringtoString(){return"MyObject{"+"att1='"+Objects.toString(att1)+'''+",att2='"+Objects.toString(att2)+'''+",att3='"+Objects.toString(att3)+'''+"}"+super.toString();}//平均性能JMH(ops/s)//(min,avg,max)=(138790,233,140791,365,142031,847)//使用JMH测量的平均性能//(minimum,average,***)=(138790,233,140791,365,142031,847)StringBuilder另一种技术是使用StringBuilder。很难说哪种技术表现更好。正如我之前所说,我使用了复杂的对象图(att1、att2和att3变量的命名是为了便于阅读),而JMH给出了大致相同的结果。后三种技术在性能方面非常接近。publicStringtoString(){finalStringBuildersb=newStringBuilder("MyObject{");sb.append("att1='").append(att1).append(''');sb.append(",att2='").append(att2).append(''');sb.append(",att3='").append(att3).append(''');sb.append(super.toString());returnsb.toString();}//使用JMH的平均性能(ops/s)//(min,avg,max)=(96073,645,141463,438,146205,910)//使用JMH测量的平均性能//(min,average,***)=(96073,645,141463,438,146205,910)GuavaGuava有一些辅助类:其中一个可以帮助您生成toString。这比纯JDKAPI性能差一点,但它可以为您提供一些额外的服务(我在这里指的是Guava):publicStringtoString(){returnObjects.toStringHelper(this).add("att1",att1).add("att2",att2).add("att3",att3).add("super",super.toString()).toString();}//JMH的平均性能(ops/s)//(min,avg,max)=(97049,043,110111,808,114878,137)//使用JMH测量的平均性能//(minimum,average,***)=(97049,043,110111,808,114878,137)CommonsLang3CommonsLang3有一些生成toString的技术:从构建器到内省器。你可以猜到,内省更容易使用,代码更少,但性能更差:("att3",att3).append("super",super.toString()).toString();}//JMH的平均性能(ops/s)//(min,avg,max)=(73510,509,75165,552,76406,370)//使用JMH测量的平均性能//(最小值,平均值,***)=(73510,509,75165,552,76406,370)publicStringtoString(){returnToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);}//JMH平均性能(ops/s)//(min,avg,max)=(31803,224,34930,630,35581,488)//平均性能usingJMH//(minimum,average,***)=(31803,224,34930,630,35581,488)publicStringtoString(){returnReflectionToStringBuilder.toString(this);}//使用JMH的平均性能(ops/s)//(min,avg,max)=(14172,485,23204,479,30754,901)//使用JMH测量的平均性能//(最小值,平均值,***)=(14172,485,23204,479,30754,901)总结现在通过JVM优化,我们可以安全地使用+连接字符串(并使用Objects.toStringtohandlenull)使用JDK中内置的实用程序类,不需要外部框架来处理空值。因此,开箱即用的JDK比本文介绍的其他技术具有更好的性能(如果您有其他框架/技术,请发表评论,我会试用)。作为总结,以下是从JMH获得的平均性能数据表(效率下降)使用技术平均操作次数/秒Concatenatestringswith'+'142.075,167Stringbuilder141.463,438Objects.toString140.791,365Guava110.111,808ToStringBuilder(追加)75.165,552ToStringBuilder(反射到字符串)34.930,630ReflectionToStringBuilder23.204,479同样,如果您经常调用toString,这很重要,否则性能真的不是问题。
