stringbuilder扩容在需存字符数超过当前数组长度时触发,新容量按“原长×2+2”计算,不足则直接设为所需最小值,本质是分配新数组、拷贝内容、更新引用,预估容量可有效减少扩容开销。

StringBuilder扩容不是“用完了才扩”,而是“要放不下时就换容器”。关键在底层数组value的长度是否够存下当前已存字符数(count)加上即将新增的字符数。够,直接写;不够,立刻扩容。
扩容触发的真实条件
每次调用append()、insert()或replace()前,都会计算:
所需最小容量 = count + 新增字符长度
只要这个值 > 当前value.length,扩容就发生。
例如:
- 默认构造
new StringBuilder()→value.length == 16,count == 0 - 追加一个20字符字符串 →
0 + 20 > 16→ 触发扩容 - 分10次追加2字符 → 前8次:count从0→16,都不超;第9次:16+2=18 > 16 → 扩容启动
新容量怎么算出来的
扩容不是简单翻倍,而是执行固定公式:
新容量 = value.length × 2 + 2
比如:
- 16 → 16×2+2 = 34
- 34 → 34×2+2 = 70
- 70 → 70×2+2 = 142
但这个结果只是“尝试值”。如果它仍小于实际需要的最小容量(比如你要一次追加200字符,而当前容量才34),那就跳过倍增,直接设为200。
+2的设计很务实:防止初始容量极小(如1或2)时,第一次扩容后还是不够用;也避免传入0容量导致数组创建失败。
扩容本质是三次内存操作
每次扩容都绕不开这三步:
- 分配一块新
char[],大小就是上面算出的新容量 - 调用
Arrays.copyOf(value, newCapacity),把旧数组内容逐字拷贝过去 - 把内部
value引用指向新数组,旧数组等待GC回收
这意味着:扩容越频繁,CPU花在复制上的时间越多,临时对象越多,GC压力越大。压测中若发现Arrays.copyOf是热点方法,基本就是扩容太勤了。
最有效的实战优化方式
不靠猜,靠预估:
- 静态拼接(如日志模板):模板本身长度 + 各变量最大可能长度(用户ID最多32位、订单号最多64位)
- 动态拼接(如拼接CSV行):单行平均长度 × 1.2,或直接加固定余量(如+64)
-
循环内复用:用
new StringBuilder(256)代替无参构造,一次预分配,全程零扩容
别过度预留——设成5000却只拼200字符,堆内存白占,反而拖慢GC。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











