strings.builder 比 string += 快得多,因 string 不可变致每次 += 都需分配新数组并拷贝,时间复杂度 o(n²),而 builder 基于可增长 []byte,追加为均摊 o(1)。

strings.Builder 为什么比 string += 快得多
因为 string 在 Go 中是不可变的,每次 += 都会分配新底层数组、拷贝旧内容、再追加,时间复杂度 O(n²);而 strings.Builder 内部用可增长的 []byte 缓冲区,追加是 amortized O(1),尤其在拼接几十次以上时差距明显。
常见错误现象:string 拼接在循环里跑得越来越慢,pprof 显示大量内存分配和拷贝;用 strings.Builder 后 CPU 和 alloc 都降一大截。
- 使用场景:日志行拼接、SQL 构建、HTTP 响应体生成、模板渲染中间结果
- 不适用场景:只拼 2–3 次、或拼接内容极小(如
"a" + "b" + "c"),编译器常会优化掉 - 注意:
Builder不是线程安全的,多 goroutine 并发写必须加锁或各自新建实例
怎么正确初始化和复用 strings.Builder
初始化时建议预估容量(用 strings.Builder.Grow() 或构造函数),避免多次扩容;复用时必须调用 Reset(),不能靠重新赋值或声明新变量来“清空”。
错误写法:var b strings.Builder; b = strings.Builder{} —— 这不会释放已分配的底层数组,只是覆盖了结构体字段,旧缓冲区还在内存里。
立即学习“go语言免费学习笔记(深入)”;
- 推荐初始化:
var b strings.Builder; b.Grow(1024)(预分配 1KB) - 复用前必须:
b.Reset(),它会把len归零但保留底层数组 - 如果不确定大小,也别省略
Grow():哪怕估个 256,也比默认 0 容量(首次扩容到 64)更友好
Builder.WriteString 和 Builder.Write 的区别与选法
WriteString() 专为字符串设计,零拷贝(直接引用底层数组);Write() 接收 []byte,会触发一次 copy,除非你刚好有 []byte 数据源,否则优先用 WriteString()。
性能影响:对短字符串(WriteString() 稳定快 10%–20%。
- 用
WriteString("hello"),不是Write([]byte("hello")) - 如果已有
data []byte,直接b.Write(data)更合适,避免转成 string 再转回 []byte -
WriteRune()和WriteByte()也存在,适合逐字符构建,但多数场景还是批量WriteString()更自然
Builder.String() 调用后还能继续用吗
能。调用 String() 只是基于当前缓冲区内容生成一个新 string,不改变 Builder 内部状态,后续仍可 WriteString()、Reset() 或再调用 String()。
容易踩的坑:误以为 String() 是“消费型”操作,结果在循环里反复调用却忘了 Reset(),导致越拼越长。
- 典型错误:
for _, s := range strs { b.WriteString(s); fmt.Println(b.String()) }→ 第二次输出包含第一次的内容 - 正确做法:循环内用完就
b.Reset(),或每次新建局部Builder - 注意:
String()返回的是只读视图,修改返回的 string 不会影响 builder,builder 也不持有该 string 的引用
Builder 底层缓冲区的生命周期完全由你控制,它不自动释放内存,Reset() 也不 shrink,所以高频复用时要注意初始容量别设太大,否则可能长期占着没用的内存。










