循环内拼接必须用stringbuilder,单次简单拼接可用string;因string不可变,每次+=都新建stringbuilder和string对象并复制数组,10000次产生上万个临时对象,gc压力大、时间复杂度近o(n²),而stringbuilder复用内部char[],仅末尾tostring一次,性能差距可达数千倍。

直接说结论:循环内拼接,别用 String;单次简单拼接,String 无压力。性能差距不是几倍,而是成百上千倍——根源在于对象创建和内存复制的开销。
为什么 String 在循环里拼接特别慢?
每次用 += 或 + 拼接,JVM 实际上会做三件事:
- 新建一个
StringBuilder对象(默认容量 16) - 把原字符串和新内容依次
append - 调用
toString()创建新String,旧对象变成垃圾
循环 10000 次,就生成 10000 个中间 String 和至少同等数量的临时 StringBuilder。GC 压力陡增,CPU 花在复制数组和回收对象上,而不是业务逻辑上。
StringBuilder 快在哪?
它内部维护一个可变的 char[] 数组,所有 append 都直接往这个数组里写:
- 初始容量够用时:零对象创建,零数组拷贝
- 容量不够时:扩容一次(通常是翻倍),再拷贝一次原内容
- 最终只调用一次
toString(),生成最终结果
实测:10 万次数字拼接,String += 耗时约 18 秒,StringBuilder.append() 仅需 5 毫秒。
怎么用 StringBuilder 才真正高效?
光换类不够,初始化和复用方式很关键:
- 别用默认构造:
new StringBuilder()初始只有 16 字符,小循环就可能扩容好几次 - 预估长度更稳:
new StringBuilder(2048)或new StringBuilder(base.length() + expectedSize) - 局部变量优先:方法内新建、用完即弃,线程安全又无锁开销
- 避免长期持有
toString()结果:它引用整个内部数组,若只需子串,用substring()或new String(char[], offset, len)拷贝所需部分
StringBuffer 是不是更稳妥?
不是。它和 StringBuilder 功能几乎一样,但每个 public 方法都加了 synchronized。单线程下纯属锁竞争和上下文切换开销,实测比 StringBuilder 慢 1.5–2 倍。真需要多线程共享,用 ThreadLocal<stringbuilder></stringbuilder> 更轻量、更可控。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











