In JDK 8, StringBuffer
class has a toStringCache
, while StringBuilder
doesn't.
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
But why?
One possible reason I can think of is that StringBuffer is already synchronized so a cache can be implemented easier.
Or maybe historically StringBuffer was implemented this way so old code depends heavily on this feature?
Given modern JVM with escape analysis and biased locking, is the difference relevant anymore?
I think that your first guess is highly accurate, since
StringBuilder
is not thread-safe and an instance can be shared across multiple threads, implementing such a cache would require additional synchronization, this would defeat the purpose ofStringBuilder
in the first place.As to why this would be needed, it boils down to the
new String(...)
constructor that is used; in case ofStringBuffer
that used the constructorString(array, boolean)
the comment says:It might help to consider the historical context.
StringBuilder
was introduced with Java 5, since it has been recognized, thatStringBuffer
isn’t well suited for its actual use cases.The newly introduced
StringBuilder
has been designed for the major use case of being constructed, used and immediately dropped afterwards, in a purely local context. Therefore, it doesn’t provide any synchronization and it doesn’t bother optimizing the rare case of itstoString()
method being called multiple times without an in-between change (when does this happen in real life at all?), especially as, indeed, providing the caching feature without sacrificing the performance advantage of no thread synchronization, is somewhere between “hard” to “impossible”.While
StringBuilder
is documented to be not thread safe, so you know inconsistent things could happen when calling methods on it concurrently, the classString
is guaranteed to be thread safe through immutability, hence, it must not be allowed thatStringBuilder
’s absence of synchronization can causes inconsistencies in already constructed strings and, not sharing the array betweenString
andStringBuilder
at all, is the safest solution.So why is this optimization there, if it hardly ever has a benefit in real life? Well, because it’s there since a very long time, most likely even since Java 1.0 and it’s not worth changing anything in the class
StringBuffer
. Its presence may not have any real advantage, but neither has removing it, which would require new testing and so on and could turn out to be the space bar overheating feature for some application…You may notice that back-then, in Java 1.x, a lot of design decision were made that may look strange today. Overusing
synchronized
in fundamental classes is one of them, this hardly ever helping optimization another one. At that time, even the implications of immutability were not well understood, which is why we have redundant methods likeString.valueOf(char[])
andString.copyValueOf(char[])
, plus the opportunity to usenew String(char[])
…