对于长度差异极大的输入,什么是最佳的StringBuffer初始容量?

6
大家下午好,我正在使用java.lang.StringBuilder来存储一些字符。我不知道要提前存储多少个字符,只知道:
  1. 60%的时间,只有(恰好)7个字符
  2. 39%的时间,大约是3500个字符
  3. 1%的时间,大约是20k个字符
如何计算应该使用的最佳初始缓冲区长度? 目前我正在使用new java.lang.StringBuilder(4000),但那只是因为我之前太懒了。

听起来默认值在大多数情况下可能是最优的。您是否可以重复使用您的StringBuilders? - Peter Lawrey
1
我们应该忘记小的效率问题,大约有97%的时间:过早的优化是万恶之源。 - Natix
@PeterLawrey 不,它们不可回收(我的意思是它们可能是,我相信这将更加优化,但这需要更改源代码)。 - Pacerier
@natix 对过早优化的担忧是我一开始插入4000并完成的原因之一。然而,现在它已经“后成熟”了,仅仅改变构造函数的参数也不是太难了吧? - Pacerier
任何更改都需要更改源代码。您可以使对象重复使用,而无需使用对象池样式进行回收。 - Peter Lawrey
显示剩余3条评论
1个回答

12
这里有两个因素:时间和内存消耗。时间大部分由调用java.lang.AbstractStringBuilder.expandCapacity()的次数影响。当然,每次调用的成本是当前缓冲区大小的线性,但我在这里简化并只计算它们:

expandCapacity()的数量(时间)

默认配置(16个字符容量)

  • 在60%的情况下,StringBuilder不会扩展容量
  • 在39%的情况下,StringBuilder会扩展8次容量
  • 在1%的情况下,StringBuilder会扩展11次容量

预期的expandCapacity数量为3.23。

初始容量为4096个字符

  • 在99%的情况下,StringBuilder不会扩展容量
  • 在1%的情况下,StringBuilder会扩展3次容量

预期的expandCapacity数量为0.03。

正如你所看到的,第二种情况似乎更快,因为它很少需要扩展StringBuilder(每100个输入扩展3次)。但请注意,第一次扩展不太明显(复制少量内存);此外,如果你将字符串以大块添加到构建器中,它会更积极地扩展,但迭代次数较少。
另一方面,内存消耗增加了:

内存消耗

默认配置(16个字符容量)

  • 在60%的情况下,StringBuilder占用16个字符
  • 在39%的情况下,StringBuilder占用4K个字符
  • 在1%的情况下,StringBuilder占用32K个字符

预期的平均内存消耗为:1935个字符。

初始容量为4096个字符

  • 在99%的情况下,StringBuilder将占用4K个字符。
  • 在1%的情况下,StringBuilder将占用32K个字符。

预期平均内存消耗为:4383个字符。


简而言之

这让我相信,将初始缓冲区扩大到4K将增加内存消耗超过两倍,同时将程序速度提高两个数量级。

总之,尝试一下吧!编写一个基准测试程序来处理不同长度和不同初始容量的百万个字符串并不难。但我认为更大的缓冲区可能是一个不错的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接