结论:四种方式按场景选:1)+ 号——少量固定拼接最简单,编译器对 a + b + c 一次分配,常量折叠场景零开销;2)strings.Builder——循环/大量拼接的标准答案,内部 []byte 增长复用,String() 零拷贝转换;3)bytes.Buffer——同 Builder 但支持读写和 io.Writer 接口,String() 会拷贝;4)fmt.Sprintf——可读性最好、性能最差(反射 + 解析格式串),非热路径随意用。
展开:性能量级:循环拼接 N 段,+ 是 O(N²)(每轮分配拷贝整个串),Builder/Buffer 均摊 O(N),差距在万次级拼接下从微秒 vs 毫秒起跳。Builder 的两个要点:Grow(n) 预分配避免多次扩容;Builder 不允许拷贝(拷贝后 Write 会 panic,vet 可查)。原理彩蛋:Builder.String() 用 unsafe.Pointer 把内部 []byte 直接转成 string 零拷贝,这是它不允许再被修改/拷贝的原因。易错点:1)fmt.Sprintf("%d", x) 与 strconv.Itoa(x) 差数倍开销,热路径用 strconv 系列;2)少量拼接(两三段)Builder 反而更啰嗦且有额外分配,直接用 +。
var b strings.Builder
b.Grow(estimated)
for _, p := range parts {
b.WriteString(p)
}
s := b.String()
追问方向:字符串底层结构(只读 []byte + len)、string(bs) 转换在 1.20+ 的优化(部分场景零拷贝)。