Comparison of Java toString Performance Optimization Solutions
Who cares about toString performance? No one! Unless you have a large amount of data in batch processing, using toString produces a lot of logs. Then, when you investigate why it is so slow, you will realize that most toString methods use introspection, which can be optimized.
However, let's take a look at what Object. toString should be considered in Javadoc: "The String Representation of the returned Object must be concise but easy to understand. We recommend that you override this method for all subclasses ". The most interesting here is "concise" and "Detailed ". Our favorite ides often generate equals, hashcode, and toString methods for us, and we usually do not care about them. In addition, these ides provide many ways to generate our own toString: String connection using the + sign), StringBuffer, StringBuilder, ToStringBuilderCommons Lang 3), ReflectionToStringBuilder (Commons Lang 3), Guava, or Objects. toString ...... Which one should I choose?
If you want to know which toString implementation method will be more efficient, do not guess, but test it! In this case, you need to use JMH. I once wrote articles about JMH on my blog, so I will not discuss details about how JMH works here.
In this benchmark, I created a complex object graph using inheritance, set, and so on), and I used all the different toString implementation methods generated by IDE, let's see which one has better performance. Just one rule of thumb: concise. No matter which technology you use), generating toSting for some or all attributes, including inheritance, dependency, or set, will have a huge impact on performance.
Connect strings with +
Let's start with the most efficient method: connect strings with +. This is once considered an evil way to use "do not use + to connect strings !!!"), It has become cool and efficient! Most of the time) the JVM compiler will compile + into a string builder. So don't hesitate. Just use it. The only drawback is that the null value will not be processed. You need to handle it yourself.
Let's take a look at the average performance calculated using JMH in the following annotations.
Public String toString (){
Return "MyObject {" +
"Att1 = '" + att1 + ''' +
", Att2 = '" + att2 + ''' +
", Att3 = '" + att3 + ''' +
"}" + Super. toString ();
}
// Average performance with JMH (ops/s)
// (Min, avg, max) = (140772,314,142 143844,717)
// Average performance measured using JMH
// (Minimum, average, maximum) = (140772,314,142, 143844,717)
Use Objects. toString to connect to a string
Java SE 7 brings the Objects class and some of its static methods. The advantage of Objects. toString is that it can process null values or even set the default value for null. Its performance is slightly lower than the previous one, but the null value can be processed:
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,140 791,365, 142031,847)
// Average performance measured using JMH
// (Minimum, average, maximum) = (138790,233,140, 142031,847)
StringBuilder
Another technology is StringBuilder. It is difficult to tell which technology has better performance. As I mentioned earlier, I have used complex object graphs att1, att2, and att3 to name variables for readability.) JMH provides more or less identical results. These three technologies are very similar in terms of performance.
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,141 463,438, 146205,910)
// Average performance measured using JMH
// (Minimum, average, maximum) = (96073,645,141 146205,910)
Guava
Guava has some helper classes: one of them can help you generate toString. This is worse than pure jdk api performance, but it can provide you with some additional services I refer to Guava here ):
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,110 114878,137)
// Average performance measured using JMH
// (Minimum, average, maximum) = (97049,043,110 114878,137)
Commons Lang3
Commons Lang3 has some techniques to generate toString: From builder to introspector. As you have guessed, introspection is easier to use and has fewer code but worse performance:
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,751 76406,370)
// Average performance measured using JMH
// (Minimum, average, maximum) = (73510,509,751, 76406,370)
Public String toString (){
Return ToStringBuilder. reflectionToString (this, ToStringStyle. SHORT_PREFIX_STYLE );
}
// Average performance with JMH (ops/s)
// (Min, avg, max) = (31803,224,349 35581,488)
// Average performance measured using JMH
// (Minimum, average, maximum) = (31803,224,349 35581,488)
Public String toString (){
Return ReflectionToStringBuilder. toString (this );
}
// Average performance with JMH (ops/s)
// (Min, avg, max) = (14172,485,232 30754,901)
// Average performance measured using JMH
// (Minimum, average, maximum) = (14172,485,232 30754,901)
Summary
Now with JVM optimization, we can safely use + to connect strings and use Objects. toString to process null ). A utility class with built-in JDK does not require an external framework to process null values. Therefore, the out-of-the-box JDK has better performance than other technologies described in this article. If you have other frameworks/technologies, please leave a comment for me to try ).
To sum up, the following is a table of average performance data obtained from JMH, which is in descending order of the highest efficiency)
Use Technology |
Average operation count/second |
Use '+' to connect to a string |
142.075, 167 |
String builder |
141.463, 438 |
Objects. toString |
140.791, 365 |
Guava |
110.111, 808 |
ToStringBuilder (append) |
75.165, 552 |
ToStringBuilder (reflectionToString) |
34.930, 630 |
ReflectionToStringBuilder |
23.204, 479 |
Again, if you call the toString method frequently, this is very important. Otherwise, performance is really not a problem.