golang字符串拼接,字符串与切片转换的几种方式比较

字符串拼接

1、直接使用+

2、使用bytes.Buffer

3、使用strings.Builder

func BenchmarkStringConcat1(b *testing.B) {
	s := ""
	for i := 0; i < b.N; i++ {
		s += "hello world"
	}
}
func BenchmarkStringConcat2(b *testing.B) {
	var buf bytes.Buffer
	for i := 0; i < b.N; i++ {
		buf.WriteString("hello world")
	}
	_ = buf.String()
}
func BenchmarkStringConcat3(b *testing.B) {
	var sb strings.Builder
	for i := 0; i < b.N; i++ {
		sb.WriteString("hello world")
	}
	_ = sb.String()
}
> go test -bench=. -benchtime=100000x -benchmem
goos: windows
goarch: amd64
pkg: test
cpu: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
BenchmarkStringConcat1-8          100000             73067 ns/op          554013 B/op          1 allocs/op
BenchmarkStringConcat2-8          100000                19.38 ns/op           53 B/op          0 allocs/op
BenchmarkStringConcat3-8          100000                 9.759 ns/op          52 B/op          0 allocs/op
PASS
ok      test    7.358s

从性能来看,strings.Builder 是最好的,而直接使用+号性能是最差的。

当使用连接符 + 拼接两个字符串时,会生成一个新的字符串并开辟新的内存空间,空间大小等于两个字符串之和。刚才的基准测试中,循环不断拼接新的字符串,这样就会不断申请内存空间,以此类推,性能就会越来越差。在字符串密集拼接场景中,使用 + 会严重降低性能。

strings.Builderbytes.Buffer 两者内存分配差不多,性能方面,前者更优。两者底层都是一个 []byte,但是 bytes.Buffer 转换字符串时重新申请了内存空间用来存放, 而 strings.Builder 使用 unsafe 包的能力直接将底层的 []byte 定义为字符串返回。

// bytes.Buffer
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:])
}

// strings.Builder
func (b *Builder) String() string {
	return unsafe.String(unsafe.SliceData(b.buf), len(b.buf))
}

// String returns a string value whose underlying bytes
// start at ptr and whose length is len.
//
// The len argument must be of integer type or an untyped constant.
// A constant len argument must be non-negative and representable by a value of type int;
// if it is an untyped constant it is given type int.
// At run time, if len is negative, or if ptr is nil and len is not zero,
// a run-time panic occurs.
//
// Since Go strings are immutable, the bytes passed to String
// must not be modified afterwards.
func String(ptr *byte, len IntegerType) string

字符串与切片转换

在golang中,字符串和切片的运行时底层数据结构非常相似,所以使用 unsafe 包可以绕过类型系统来实现类型转换。

type StringHeader struct {
	Data uintptr
	Len  int
}
type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

1、字符串转换成切片

func BenchmarkStringToByte1(b *testing.B) {
	s := "hello world"
	for i := 0; i < b.N; i++ {
		s2b1(s)
	}
}
func s2b1(s string) []byte {
	return []byte(s)
}
func BenchmarkStringToByte2(b *testing.B) {
	s := "hello world"
	for i := 0; i < b.N; i++ {
		s2b2(s)
	}
}
func s2b2(s string) (b []byte) {
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh.Data = sh.Data
	bh.Len = sh.Len
	bh.Cap = sh.Len
	return
}
> go test -bench=BenchmarkStringToByte -benchtime=10000000x -benchmem
goos: windows
goarch: amd64
pkg: test
cpu: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
BenchmarkStringToByte1-8        10000000                 4.904 ns/op           0 B/op          0 allocs/op
BenchmarkStringToByte2-8        10000000                 0.2929 ns/op          0 B/op          0 allocs/op

2、切片转换成字符串

func BenchmarkByteToString1(b *testing.B) {
	bb := []byte(`hello world`)
	for i := 0; i < b.N; i++ {
		b2s1(bb)
	}
}
func b2s1(b []byte) string {
	return string(b)
}
func BenchmarkByteToString2(b *testing.B) {
	bb := []byte(`hello world`)
	for i := 0; i < b.N; i++ {
		b2s2(bb)
	}
}
func b2s2(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))

}
> go test -bench=BenchmarkByteToString -benchtime=10000000x -benchmem
goos: windows
goarch: amd64
pkg: test
cpu: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
BenchmarkByteToString1-8        10000000                 6.078 ns/op           0 B/op          0 allocs/op
BenchmarkByteToString2-8        10000000                 0.2929 ns/op          0 B/op          0 allocs/op

性能上提升还是很明显的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值