Performance Day One
Performance is often underrated in the early and middle stages of development. When the product is almost finished, components have been integrated and working together in a complex solution. Then any performance issues become more visible.
Performance can be addressed at the architecture, design, or code level. In this blog, we focus on the code level where small changes can be done with a small cost but significant gain.
Comparison of strings
Comparing strings is widely used. In the following part, we evaluate different approaches to string comparison. We chose the most popular core Java 11 implementations and the widely used Apache StringUtils library.
The following was done:
- Compared two 12-character long strings with a different last character,
- Used System.nanoTime() to measure the time.
Time:[picos] | Time:[ms] | Time:[ms] | Time:[ms] | ||
Label | Equals | 1 time | 5 million times | 10 million times | 20 million times |
E1 | a.equals(b) | 0.386 | 1.929 | 3.729 | 7.71 |
E2 | a.compareTo(b) | 0.426 | 1.937 | 3.7 | 8.519 |
E3 | Objects.equals(a, b) | 0.409 | 1.867 | 3.915 | 8.176 |
E3 | StringUtils.equals(a, b) | 1.190 | 5.912 | 11.472 | 23.801 |
EqualsIgnoreCase | |||||
IG1 | a.equalsIgnoreCase(b) | 18.751 | 93.19 | 191.512 | 375.027 |
IG2 | a.compareToIgnoreCase(b)==0 | 11.537 | 57.488 | 108.275 | 230.744 |
IG3 | StringUtils.equalsIgnoreCase(a,b) | 16.788 | 103.436 | 197.758 | 335.761 |
IG4 | StringUtils.compare(a, b) == 0 | 7.153 | 36.585 | 80.412 | 143.052 |
The results shows we avoid comparing strings using ignore case when possible and use simple equals instead. However, there is a clear winner when needing ignore case: StringUtils.compare(a, b) == 0 is much faster than anything else. If we can’t use this Apache library, stick to the Java a.compareToIgnoreCase(b) == 0.
String concatenation
String concatenation can be done in multiple ways, and we will present the impact of chosen approaches on performance.
The following was done:
- Concatenate two 12-character long strings,
- Used System.nanoTime() to measure the time,
- A new builder was created for each append.
Time:[ms] | Time:[ms] | Time:[ms] | |
Compare method | 1 million times | 5 million times | 10 million times |
concat | 51.307 | 79.989 | 131.339 |
builder | 12.164 | 41.178 | 81.699 |
+ | 24.185 | 46.73 | 78.876 |
format | 260.557 | 777.733 | 1522 |
Using String.format is expensive, so we should use it with caution; a simple add or string builder might be better. However, String.format doesn’t just concatenate, it does much more and is easier to read and maintain; so it might be the right choice. You have to decide.
Conclusion
This article covered two common usages around strings, and as we saw, simple changes can make a big difference. Performance isn’t the only consideration, it is essential to consider other factors like maintainability.
Sometimes, slightly less performant methods with higher readability are a better choice.
Other times, the worst performant method may be the best choice. For example, String.format might be easier to read and maintain, and provide additional functionality, making the worst performing choice the right one.
Dariusz Czajkiewicz
Senior SDET
Watch or read our other posts at Kimputing Blogs. You’ll find everything from Automated testing to CenterTest, Guidewire knowledge to general interest. We’re trying to help share our knowledge from decades of experience.