贝利信息

Golangbytes.Buffer与strings.Builder如何选择

日期:2026-01-06 00:00 / 作者:P粉602998670
strings.Builder 更适合纯字符串拼接,轻量高效、零值安全、避免内存逃逸;bytes.Buffer 功能更全,支持I/O接口、中间读取和多种写入方式,但有额外开销。

strings.Builder 更适合纯字符串拼接

当目标是构建一个最终为 string 的结果,且过程中不涉及二进制数据、不需读取中间状态、也不需要像 io.Reader 那样流转时,strings.Builder 是更轻量、更高效的选择。它底层复用 []byte,但禁止直接访问底层数组,避免了意外修改和内存逃逸。

var b strings.Builder
b.Grow(128)
b.WriteString("hello")
b.WriteString(" ")
b.WriteString("world")
result := b.String() // 此后不应再对 b 调用 Write* 方法

bytes.Buffer 适用场景更广,但有额外成本

bytes.Buffer 实现了 io.Readerio.Writerio.ByteReaderio.RuneScanner 等多个接口,能无缝接入标准库 I/O 流程。如果你需要把拼接过程当作流处理(比如传给 json.Encodertemplate.Execute、或做部分读取),它几乎是唯一选择。

var b bytes.Buffer
b.WriteString("value: ")
fmt.Fprint(&b, 42)
b.WriteByte('\n')
// 可以接着传给其他函数:
json.NewEncoder(&b).Encode(map[string]int{"x": 1}) // ❌ 错误:Encoder 需要 io.Writer,但这里 b 已含前置内容
// 正确做法是另起一个 Buffer,或用 Reset()

别在 strings.Builder 上调用 Bytes() 或试图取地址

strings.Builder 没有公开的 Bytes() 方法,强行通过反射或 unsafe 获取底层数组属于未定义行为,且 Go 1.20+ 对其内部结构做了调整,极易出错。如果业务逻辑中突然需要「在拼接中途读取原始字节」或「把 Builder 当作 buffer 复用」,说明设计上已偏离它的定位 —— 这时应直接换用 bytes.Buffer

性能差异在高频小拼接中才明显

单次拼接几十个字符串,两者差距几乎不可测;但在循环内每轮拼接上百次、持续数万轮的场景下,strings.Builder 因更少的方法调用、无接口动态分发、无读取逻辑,实测快 10%–25%,GC 压力也略低。

立即学习“go语言免费学习笔记(深入)”;

真正的取舍点不在性能数字,而在「你是否需要 Builder 所拒绝提供的能力」。只要没用到 ReadWriteByteReset 或中间读取,就用 strings.Builder;一旦出现这些需求,切换过去并不贵,但提前选错会埋下隐性维护成本。