StringBuilder扩容有明确触发条件、固定计算逻辑和清晰内存步骤,发生在操作前容量检查时,当count+新增长度>当前数组长度即触发,新容量按公式newCapacity = (oldCapacity << 1) + 2计算。

StringBuilder 的扩容不是“突然变大”,而是有明确触发条件、固定计算逻辑和清晰内存步骤的过程。理解它,能帮你避开频繁扩容带来的性能抖动。
内部存储结构:char[] + count 组合
StringBuilder 本身不存数据,真正干活的是父类 AbstractStringBuilder 中的两个关键字段:
-
char[] value:实际存放字符的数组,
capacity()返回的就是它的长度 -
int count:当前已写入的有效字符个数,
length()返回的就是这个值
初始容量默认为 16(无参构造),此时 value.length == 16,但 count == 0。随着 append() 调用,count 逐步增长,直到逼近甚至等于 value.length,扩容就该发生了。
扩容触发时机:不是“满了才扩”,而是“不够就扩”
扩容发生在每次修改操作前的容量检查阶段,典型场景包括:
立即学习“Java免费学习笔记(深入)”;
-
append(str)时:若count + str.length() > value.length -
insert(index, str)时:若count + str.length() > value.length(注意不是只看 index) -
replace()或delete()后再追加,也可能间接触发
关键点:判断依据是操作后总长度是否超出当前数组长度,而不是当前是否“已满”。哪怕 count == 15,追加一个长度为 2 的字符串,也会触发扩容。
新容量怎么算?公式背后有逻辑
扩容不是简单翻倍,标准计算逻辑如下:
- 先按公式算基础容量:
newCapacity = (oldCapacity (即 <code>old * 2 + 2) - 再与最小所需容量(
count + 新增长度)比较 - 取两者中较大者作为最终容量
- 最后校验上限:
MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),超限会抛OutOfMemoryError
例如:初始容量 16,追加 20 个字符 → 所需最小容量为 20 → 基础扩容得 34 → 取 max(20, 34) = 34 → 成功扩容到 34。
扩容过程三步走:分配、复制、切换
扩容本质是一次“换容器”操作,包含三个原子动作:
- 调用
Arrays.copyOf(value, newCapacity)分配新char[]并完成数据迁移 - 旧数组失去引用,等待 GC 回收
-
value字段指向新数组,count不变,后续操作继续写入
这个过程涉及堆内存分配和数组拷贝,开销明显。10 万次追加若触发 10 次扩容,可能比预分配一次更慢。


















