StringBuilder扩容触发条件明确:append或insert后总长度超当前数组长度即扩容,如count+len>value.length或pos+len>value.length,不取决于已用容量。

StringBuilder 的扩容机制不是“按需分配”,而是有明确触发条件和计算逻辑的。关键在于:它不看“用了多少”,而看“追加后会不会超”。只要一次 append、insert 等操作导致 当前字符数 + 新增字符数 > 当前底层数组长度,就立刻触发扩容。
扩容触发的三个典型场景
以下任一情况发生,ensureCapacityInternal() 就会被调用,进而判断是否扩容:
- append() 追加时总长度超限:比如当前 value.length = 20,已有 18 个字符(count = 18),再 append("xyz")(长度为 3),18 + 3 = 21 > 20 → 触发扩容
- insert() 插入位置偏移超限:比如在索引 15 处 insert("ab"),15 + 2 = 17 > 当前数组长度 16 → 触发扩容(即使前面只用了 10 个字符)
- 构造时指定容量但后续内容远超预期:如 new StringBuilder(4),接着 append("hello world")(11 字符),4
扩容不是简单翻倍,而是分两步算新容量
newCapacity(minimumCapacity) 方法实际执行如下逻辑:
- 先按公式算一个候选值:newCap = oldCapacity × 2 + 2(等价于
oldCapacity << 1 + 2) - 再比较:如果 newCap ≥ minimumCapacity(即刚算出的容量够用),就用 newCap
- 否则直接取 minimumCapacity(例如原容量 16,要存 100 字符,16×2+2=34 仍不够,就直接扩容到 100)
初始容量怎么影响扩容频率
初始容量设得越接近最终总长度,扩容次数就越少。三种常见构造方式的默认起点不同:
-
无参构造:capacity = 16(
new StringBuilder()) -
传 int 构造:capacity = 指定值(
new StringBuilder(100)) -
传 String 构造:capacity = str.length() + 16(
new StringBuilder("abc")→ 初始容量 19)
例如拼接 200 字符的文本,从 16 起步会经历约 4 次扩容(16→34→70→142→200),而直接设为 200 就零扩容。
如何主动避免被动扩容
不依赖自动扩容,可提前干预:
- 用 ensureCapacity(int minCapacity) 手动预留:如已知最终约 500 字符,可在创建后立即调用
sb.ensureCapacity(500),一次到位分配数组 - 构造时就预估:对日志拼接、SQL 组装、模板渲染等可预判长度的场景,优先使用
new StringBuilder(估算值) - 注意“看似够用实则不够”:比如初始容量 100,当前用了 95,append 6 字符 → 95+6=101 > 100 → 仍会扩容

















