Who cares about toString performance ?, Are you concerned about toString performance?
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 do when Javadoc recalled: "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, ToStringBuilder (Commons 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 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 (as follows), 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 method of use ("do not use + to connect strings !!!"), It has become cool and efficient! Now the JVM Compiler (most of the time) 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.
1 public String toString () {2 return "MyObject {" + 3 "att1 = '" + att1 + ''' + 4 ", att2 = '"+ att2 + ''' + 5", att3 =' "+ att3 + ''' + 6"} "+ super. toString (); 7} 8 9 // Average performance with JMH (ops/s) 10 // (min, avg, max) = (140772,314,142 143844,717) 11 // average performance tested using JMH 12 // (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:
1 public String toString () {2 return "MyObject {" + 3 "att1 = '" + Objects. toString (att1) + ''' + 4 ", att2 = '" + Objects. toString (att2) + ''' + 5 ", att3 = '" + Objects. toString (att3) + ''' + 6 "}" + super. toString (); 7} 8 9 // Average performance with JMH (ops/s) 10 // (min, avg, max) = (138790,233,140 791,365, 142031,847) 11 // average performance tested using JMH 12 // (minimum, average, maximum) = (138790,233,140 791,365, 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 (the names of att1, att2, and att3 variables are for readability). JMH provides more or less identical results. These three technologies are very similar in terms of performance.
1 public String toString () {2 final StringBuilder sb = new StringBuilder ("MyObject {"); 3 sb. append ("att1 = '"). append (att1 ). append ('''); 4 sb. append (", att2 = '"). append (att2 ). append ('''); 5 sb. append (", att3 = '"). append (att3 ). append ('''); 6 sb. append (super. toString (); 7 return sb. toString (); 8} 9 10 // Average performance with JMH (ops/s) 11 // (min, avg, max) = (96073,645,141 463,438, 146205,910) 12 // average performance tested using JMH 13 // (minimum, average, maximum) = (96073,645,141 463,438, 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)
1 public String toString () {2 return Objects. toStringHelper (this) 3. add ("att1", att1) 4. add ("att2", att2) 5. add ("att3", att3) 6. add ("super", super. toString ()). toString (); 7} 8 9 // Average performance with JMH (ops/s) 10 // (min, avg, max) = (97049,043,110 114878,137) 11 // average performance tested using JMH 12 // (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:
1 public String toString () {2 return new ToStringBuilder (this) 3. append ("att1", att1) 4. append ("att2", att2) 5. append ("att3", att3) 6. append ("super", super. toString ()). toString (); 7} 8 9 // Average performance with JMH (ops/s) 10 // (min, avg, max) = (73510,509,751 65,552, 76406,370) 11 // average performance tested using JMH 12 // (minimum, average, maximum) = (73510,509,751 65,552, 76406,370) 13 14 public String toString () {15 return ToStringBuilder. reflectionToString (this, ToStringStyle. SHORT_PREFIX_STYLE); 16} 17 18 // Average performance with JMH (ops/s) 19 // (min, avg, max) = (31803,224,349 35581,488) 20 // The average performance tested using JMH 21 // (minimum, average, maximum) = (31803,224,349 35581,488,) 22 23 public String toString () {24 return ReflectionToStringBuilder. toString (this); 25} 26 27 // Average performance with JMH (ops/s) 28 // (min, avg, max) = (14172,485,232 30754,901) 29 // average performance tested using JMH 30 // (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 to me to try it out ).
To sum up, the following is a table of average performance data obtained from JMH (descending from the most efficient)
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.