In the JDK source for AbstractStringBuilder the append(int)
method is implemented in a way that does not allocate unnecessary memory (writing directly to the internal char[]
using Integer.getChars
).
But the implementation for insert
uses String.valueOf
which returns a new string and then copies that data into the array. Creating a garbage string.
Isn't one of the points of a StringBuilder
to mitigate the garbage impact of concatenating strings. And a garbage-free implementation of insert
seems trivial to implement. So why isn't it?
This seems to be the case in both the Oracle JDK and OpenJDK. It is even mentioned in the javadoc:
The overall effect is exactly as if the second argument were converted to a string by the method String.valueOf(int), and the characters of that string were then inserted into this character sequence at the indicated offset.
If you compare insert(int,int)
with the other insert
methods, you will recognize that the implementation approach was to keep it simple, reducing the code duplication by letting a single insert(String)
method do all the actual work.
This is a reasonable approach in software development, first create a straight-forward general implementation handling all cases, then do profiling with real applications & scenarios to find the places where creating a specialized, perhaps not so simple, optimized version has a benefit. It seems that no search was made to find other places where the same optimized implementation could be used. Considering that the preceding measurement apparently didn’t reveal insert(int,int)
as an important hot spot, that doesn’t hurt.
To evaluate the situation, it is important to understand that not the number of temporary objects makes repeated String.concat
expensive. Excessive creation of temporary objects may add fuel to the flames so it’s still worth avoiding it if there is a simple option. But the problem with repeated String.concat
calls is that the creation of each temporary string instance implies copying of the entire character data that makes up the string contents. The more concat
calls your string building does, the closer you will get to a quadratic time complexity.
The StringBuilder
solves this by using a mutable buffer. It’s still copied when the capacity is exhausted, but by using a factor to determine the new capacity, the overall time complexity stays linear. The implementation of insert
doesn’t change this fundamental principle. The temporary String
instance only causes a copying of the right-hand side, so it only introduces a constant factor of two rather than changing the time complexity.
But don’t forget that insert
bears a copying of the buffer’s subsequent character data in principle. If you repeatedly insert at the beginning of the buffer, you’ll steer towards a quadratic time complexity, regardless of how much optimized the underlying implementation is. So the factor of two will become negligible anyway if you’re doing this excessively.