为什么 StringBuffer 有 toStringCache 而 StringBuilder 没有?
Why StringBuffer has a toStringCache while StringBuilder not?
在 JDK 8 中,StringBuffer
class 有一个 toStringCache
,而 StringBuilder
没有。
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
但是为什么呢?
我能想到的一个可能原因是 StringBuffer 已经同步,因此可以更容易地实现缓存。
或者也许在历史上 StringBuffer 就是这样实现的,所以旧代码在很大程度上依赖于这个特性?
鉴于具有逃逸分析和偏向锁定的现代 JVM,差异是否不再相关?
我认为你的第一个猜测是高度准确的,因为 StringBuilder
不是线程安全的,并且一个实例可以跨多个线程共享,实现这样的缓存将需要额外的同步,这将违背目的StringBuilder
排在首位。
至于为什么需要这样做,归结为使用的 new String(...)
构造函数;在 StringBuffer
使用构造函数 String(array, boolean)
的情况下,评论说:
Package private constructor which shares value array for speed.
考虑历史背景可能会有所帮助。 StringBuilder
是与 Java 5 一起引入的,因为人们已经认识到,StringBuffer
不太适合其实际用例。
新推出的 StringBuilder
专为在纯本地环境中构建、使用并随后立即删除的主要用例而设计。因此,它不提供任何同步,也不会费心优化 toString()
方法被多次调用而没有中间更改的罕见情况(这在现实生活中什么时候发生?),尤其是,事实上,在不牺牲无线程同步的性能优势的情况下提供缓存功能,介于“困难”和“不可能”之间。
虽然 StringBuilder
被记录为非线程安全的,因此您知道在其上并发调用方法时可能会发生不一致的事情,但 class String
保证是线程安全的因此,通过不变性,绝不能允许 StringBuilder
缺少同步会导致已构建的字符串不一致,并且根本不在 String
和 StringBuilder
之间共享数组,是最安全的解决方案。
那么,如果这种优化在现实生活中几乎没有任何好处,那么为什么会有这种优化呢?好吧,因为它已经存在很长时间了,很可能甚至从 Java 1.0 开始就存在,并且不值得更改 class StringBuffer
中的任何内容。它的存在可能没有任何真正的优势,但也没有删除它,这将需要新的测试等等,并且可能会成为某些应用程序的 space bar overheating feature…
您可能会注意到,当时 Java 1.x 做出的许多设计决策在今天看来可能很奇怪。在基础 classes 中过度使用 synchronized
就是其中之一,这几乎不会帮助优化另一个。那时,连不变性的含义都没有被很好地理解,这就是为什么我们有像 String.valueOf(char[])
and String.copyValueOf(char[])
, plus the opportunity to use new String(char[])
…
这样的冗余方法
在 JDK 8 中,StringBuffer
class 有一个 toStringCache
,而 StringBuilder
没有。
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
但是为什么呢?
我能想到的一个可能原因是 StringBuffer 已经同步,因此可以更容易地实现缓存。
或者也许在历史上 StringBuffer 就是这样实现的,所以旧代码在很大程度上依赖于这个特性?
鉴于具有逃逸分析和偏向锁定的现代 JVM,差异是否不再相关?
我认为你的第一个猜测是高度准确的,因为 StringBuilder
不是线程安全的,并且一个实例可以跨多个线程共享,实现这样的缓存将需要额外的同步,这将违背目的StringBuilder
排在首位。
至于为什么需要这样做,归结为使用的 new String(...)
构造函数;在 StringBuffer
使用构造函数 String(array, boolean)
的情况下,评论说:
Package private constructor which shares value array for speed.
考虑历史背景可能会有所帮助。 StringBuilder
是与 Java 5 一起引入的,因为人们已经认识到,StringBuffer
不太适合其实际用例。
新推出的 StringBuilder
专为在纯本地环境中构建、使用并随后立即删除的主要用例而设计。因此,它不提供任何同步,也不会费心优化 toString()
方法被多次调用而没有中间更改的罕见情况(这在现实生活中什么时候发生?),尤其是,事实上,在不牺牲无线程同步的性能优势的情况下提供缓存功能,介于“困难”和“不可能”之间。
虽然 StringBuilder
被记录为非线程安全的,因此您知道在其上并发调用方法时可能会发生不一致的事情,但 class String
保证是线程安全的因此,通过不变性,绝不能允许 StringBuilder
缺少同步会导致已构建的字符串不一致,并且根本不在 String
和 StringBuilder
之间共享数组,是最安全的解决方案。
那么,如果这种优化在现实生活中几乎没有任何好处,那么为什么会有这种优化呢?好吧,因为它已经存在很长时间了,很可能甚至从 Java 1.0 开始就存在,并且不值得更改 class StringBuffer
中的任何内容。它的存在可能没有任何真正的优势,但也没有删除它,这将需要新的测试等等,并且可能会成为某些应用程序的 space bar overheating feature…
您可能会注意到,当时 Java 1.x 做出的许多设计决策在今天看来可能很奇怪。在基础 classes 中过度使用 synchronized
就是其中之一,这几乎不会帮助优化另一个。那时,连不变性的含义都没有被很好地理解,这就是为什么我们有像 String.valueOf(char[])
and String.copyValueOf(char[])
, plus the opportunity to use new String(char[])
…