预设stringbuilder容量核心是避免扩容时的数组拷贝开销;capacity()为底层数组总长,length()为已写入字符数;扩容公式为oldcapacity×2+2,触发条件是length()+新增长度>capacity(),预估时需加余量并向上取整。

预设 StringBuilder 容量,核心是为了避开数组拷贝带来的性能抖动——不是每次 append 都慢,而是扩容那一刻集中消耗 CPU 和内存。
容量和长度别搞混
capacity() 返回的是内部 char[] 数组的总长度,length() 才是当前已写入的字符数。比如:
- new StringBuilder("abc") → length() 是 3,capacity() 是 19(3 + 默认预留 16)
- new StringBuilder() → capacity() 直接是 16,length() 是 0
只要 length() + 新增字符数 > capacity(),下一次 append 就会触发扩容。真正耗时的操作是 System.arraycopy 拷贝已有内容,不是算新容量。
扩容不是加16,而是翻倍加2
扩容公式是:newCapacity = oldCapacity × 2 + 2(JDK 11+ 逻辑更平滑,但仍是非线性增长)。实际执行分两步:
- 先按公式算出候选值(如 16 → 34 → 70 → 142…)
- 若该值仍不够存下所有内容,就直接取所需最小容量(比如要拼 200 字符,142 不够,就一步扩到 200)
这意味着:从默认 16 开始拼 200 字符,大概率经历 4 次扩容,每次都要拷贝全部已有字符;而 new StringBuilder(200) 可全程零拷贝。
怎么预估才靠谱
预估容量不是死算,得留余量、向上靠整:
- 拼固定字符串集合(如 List
):sum 所有字符串 length,再加 10%~15% 余量 - 拼带分隔符的文本(如 CSV、SQL):把分隔符数量 × 分隔符长度也算进去
- 不确定但数据量大:宁可稍高估,比如取 1024、2048、4096 这类 2 的幂,避免多次翻倍跳变
例如拼 100 条平均 20 字符的记录 + 99 个逗号:100×20 + 99 = 2099 → 向上取整到 2048 或直接设 2200 更稳妥。
哪些场景值得动手设容量
不是所有拼接都要优化,但在这些地方收益明显:
- 循环内拼接(日志组装、SQL 构建、模板生成),且循环次数 ≥ 5、单次追加 ≥ 10 字符
- 跨方法调用或递归拼接,编译器无法做字符串常量折叠
- 批量导出(如万行 CSV、千条 JSON)、消息体组装等可预估总量的场景
- ThreadLocal 缓存 StringBuilder 时,固定设一个合理容量(如 1024),复用前调用 setLength(0)
单次拼接、短字符串、低频调用,用默认构造器完全没问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











