关于go的字符串追加

通常使用go的字符串追加方式有三种

  1. 字符串通过+追加
  2. 字符串通过fmt.Sprintf拼接字符串
  3. 通过string.Builder+预分配空间追加
// 长度为1000的字符串切片
data = []string{....}

// with fmt
for _, t := range data {
  s = fmt.Sprintf("%s%s", s, t)
}

// with append
for _, t := range data {
  s += t
}

// string.Builder
var builder strings.Builder
builder.Grow(totalLen) // 预分配
for _, s := range data {
  builder.WriteString(s)
}
_ = builder.String()

通过阅读源码可知

fmt.Sprintf过程每次会newPrinter,然后在遍历参数,对每个参数做append操作

普通append会根据新加字符串长度扩容底层rune切片,实现字符串的追加

builder会直接操作底层切片,在预分配后,可减少扩容次数,并更加高效利用内存

基准测试文件

package main

import (
	"fmt"
	"strings"
	"testing"
)

// 生成测试数据
func generateData(n int) []string {
	data := make([]string, n)
	for i := 0; i < n; i++ {
		data[i] = "test-string-"
	}
	return data
}

// 带预分配的基准测试
func BenchmarkWithPreAlloc(b *testing.B) {
	data := generateData(1000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		totalLen := 0
		for _, s := range data {
			totalLen += len(s)
		}
		var builder strings.Builder
		builder.Grow(totalLen) // 预分配
		for _, s := range data {
			builder.WriteString(s)
		}
		_ = builder.String()
	}
}

// 无预分配的基准测试 with append
func BenchmarkWithoutPreAlloc(b *testing.B) {
	data := generateData(1000)

	b.ResetTimer()
	var s string
	for i := 0; i < b.N; i++ {
		for _, t := range data {
			s += t
		}
	}
}

// 无预分配的基准测试 with fmt
func BenchmarkWithoutPreAllocWithFmt(b *testing.B) {
	data := generateData(1000)

	b.ResetTimer()
	var s string
	for i := 0; i < b.N; i++ {
		for _, t := range data {
			s = fmt.Sprintf("%s%s", s, t)
		}
	}
}

测试结果

goos: darwin
goarch: arm64
pkg: test
cpu: Apple M2
BenchmarkWithPreAlloc-8                   282478              4194 ns/op           12288 B/op          1 allocs/op
BenchmarkWithoutPreAlloc-8                   100          29212843 ns/op        604022992 B/op      1040 allocs/op
BenchmarkWithoutPreAllocWithFmt-8            100          73823327 ns/op        1207757819 B/op     4920 allocs/op
PASS
ok      test    12.647s

由结果可见,通过string.Builder+预分配模式极大的提高了效率

总结:

底层原理还是通过对切片做了自定义长度,避免了频繁自动扩容造成的时间浪费于内存浪费

posted @ 2025-03-25 23:17  aliliusi  阅读(44)  评论(0)    收藏  举报