应使用StringBuilder替代String循环拼接,因其可变且避免频繁对象创建;需预估容量、循环外创建、最后调用toString(),并优先选用String.join等高层API。

直接用 String 在循环里拼接(比如 s += "x")会严重拖慢性能,核心原因是 String 不可变——每次拼接都新建对象,1000 次循环就产生 1000 个临时字符串,触发大量 GC 和内存分配。真正高效的做法是换掉它,而不是修修补补。
用 StringBuilder 替代 + 拼接
这是最直接、最常用的解法。StringBuilder 是可变的,内部用 char[] 缓冲区追加内容,不生成中间字符串。
- 循环外创建实例,不要在循环体内 new:避免重复构造开销和默认容量(16)带来的频繁扩容
- append 基本类型(如
int、boolean)时,直接写sb.append(i),不用String.valueOf(i)—— 少一次装箱和对象创建 - 单线程场景下,一律选 StringBuilder;只有多个线程共用同一个缓冲区时,才考虑 StringBuffer(但更推荐每个线程独立实例)
预估并设置初始容量
默认容量太小,数据一多就扩容:每次扩容要申请新数组、复制旧内容,代价叠加。比如拼接 500 条日志,每条平均 60 字符,加上分隔符约需 32000 字符,就该写 new StringBuilder(32000)。
- 估算公式:基础长度 + 预期总字符数 + 10% 左右余量
- 不确定精确值也没关系,宁可略大一点,也比反复扩容强
只在最后调用一次 toString()
toString() 是“快照”操作,会完整复制当前内部数组。如果写在循环里,等于每轮都做一次全量拷贝,彻底废掉 StringBuilder 的优势。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 所有 append 完成后再调用
sb.toString() - 调试时想看中间结果,可用
sb.length()或sb.substring(0, Math.min(100, sb.length())),不触发复制
优先考虑更高层的替代方案
如果拼接的是已有集合或数组,别手动写循环,JDK 提供了更安全、语义更清晰的工具:
-
String.join(",", list):简洁,自动处理 null(转为空字符串) -
StringJoiner:支持前缀、分隔符、后缀,适合构建带格式的字符串(如 JSON 片段、SQL IN 列表) -
list.stream().collect(Collectors.joining(",")):函数式风格,底层仍用 StringBuilder,但逻辑封装更稳
不复杂但容易忽略。


















