stringbuilder 拼接大量文本时应预调 ensurecapacity() 避免频繁扩容;capacity() 返回内部数组总长度而非字符串长度;预估最终长度并预留5%~10%余量,一次调用即可。

StringBuilder 在拼接大量文本时,内部字符数组会动态扩容。频繁扩容(尤其是小步增长)会触发多次数组复制,显著拖慢性能。合理使用 capacity() 和 ensureCapacity(int) 可以提前预留足够空间,避免重复分配与拷贝。
理解 capacity() 的实际含义
capacity() 返回的是当前内部数组的总长度(不是字符串长度 length()),即还能容纳多少个额外字符而不触发扩容。例如:
StringBuilder sb = new StringBuilder("abc"); // length=3, capacity=16(默认初始容量)
sb.ensureCapacity(100); // 扩容后 capacity ≥ 100,比如变成 100 或 112
System.out.println(sb.capacity()); // 输出 ≥100 的值
注意:JDK 实现中扩容策略通常是「当前 capacity × 2 + 2」,但具体数值不需强记,关键是通过 capacity() 观察当前余量,辅助判断是否需要预分配。
在拼接前预估并调用 ensureCapacity()
如果你能大致预估最终字符串长度(比如日志批量合并、XML/JSON 拼装、模板渲染),就应在循环或拼接开始前调用 ensureCapacity(minCapacity),确保容量 ≥ 预估最大长度:
- 预估方法包括:统计待拼接片段总字符数、按字段平均长度×数量、参考历史最大值
- 建议多留 5%~10% 余量,避免边界情况下的意外扩容
- 只需调用一次——
ensureCapacity()是幂等的,多次调用不会重复扩容
避免“边拼边查”的低效模式
不要在循环中反复检查 capacity() 并调用 ensureCapacity(),例如:
// ❌ 错误示范:每次检查都可能触发无谓判断,且扩容逻辑分散
for (String s : list) {
if (sb.capacity()
应改为先统计算法总长,再一次性预分配:
// ✅ 正确做法:集中预估 + 一次 ensureCapacity
int totalEstimate = list.stream().mapToInt(String::length).sum() + 100; // +100 为余量
sb.ensureCapacity(totalEstimate);
for (String s : list) {
sb.append(s);
}
结合构造函数更简洁
如果预估很明确,直接用带初始容量的构造函数更干净:
-
new StringBuilder(estimatedTotalLength)—— 初始化即分配,省去后续ensureCapacity() - 若不确定最小值,仍推荐先
ensureCapacity(),比默认构造(capacity=16)后多次扩容高效得多 - 注意:即使传入 0,构造函数也会设为 16;传入负数会抛
IllegalArgumentException










