关键在于预设合理容量以全程避免扩容;capacity()是底层数组总长,length()是已写入字符数;扩容公式为old×2+2,触发条件是length()+新增长度>capacity(),预估应加10%~15%余量并向上取整至2的幂。

关键在于预设合理容量,让 StringBuilder 从一开始就有足够空间容纳全部内容,全程避免扩容。
搞清 capacity 和 length 的区别
capacity() 返回的是内部 char[] 数组的总长度,即已分配的内存空间;length() 才是当前已写入的字符数。比如:
new StringBuilder("abc") → length() 是 3,capacity() 是 19(3 + 默认预留 16);
new StringBuilder() → capacity() 直接是 16,length() 是 0。
只要 length() + 新增字符数 > capacity(),下一次 append 就会触发扩容——真正耗时的是 System.arraycopy 拷贝已有内容,不是计算新容量本身。
扩容代价比想象中高
扩容公式是 newCapacity = oldCapacity × 2 + 2(JDK 11+ 更平滑但仍非线性)。实际分两步:
• 先按公式算候选值(如 16 → 34 → 70 → 142…)
• 若仍不够用,就直接扩到所需最小容量(比如要存 200 字符,142 不够,就一步到 200)
从默认 16 开始拼 200 字符,大概率经历 4 次扩容,每次都要拷贝全部已有内容;而 new StringBuilder(200) 可全程零拷贝。
预估容量的实用方法
目标不是绝对精确,而是让最终 length 落在 capacity 的 80%~90% 区间内:
• 固定集合拼接:累加所有待拼字符串的 length(),再加 10%~15% 波动余量(如日志字段可能含空值或超长 ID)
• 模板拼接:模板字面长度 + 各变量最大可能长度(如 "id={}&name={}" + id.length() + name.length() + 10)
• 向上取整到 2 的幂:1000 → 1024,3000 → 4096(内存对齐友好,部分 JVM 优化更充分)
• 完全不可知但有上限:按上限设;完全不可知又无上限:至少设为 128 或更高,避免前几次低效扩容
立即学习“Java免费学习笔记(深入)”;
避开常见误用陷阱
• 不要在构造后才估算长度——容量必须在 new 时指定才有效
• 避免设太小(仍频繁扩容)或太大(如预估 10MB 实际只用 10KB),浪费堆内存,尤其在线程局部短生命周期实例中
• 不要把 StringBuilder 声明为 static、成员变量或放进缓存里长期持有,否则底层数组会持续膨胀且无法回收
• 循环拼接务必在循环外创建 StringBuilder 并复用,而不是每轮都 new 一个


















