开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第34天,点击查看活动详情
前言
在我们进行 Go 开发的过程中,通常会用到一些数据结构来实现我们的需求,比较常用的有 string、slice (切片)、map(字典)等。对于它们的实现,我们是否又清楚呢?而对于它们的使用,我们又如何发挥出它们的高性能呢?接下来我们通过这篇文章来一起学习探讨~
string 字符串
结构
string 其实就是一个不可变的 byte 数组,其结构为
- 一个 string 在内存中占用16字节
- 0~8 字节的 str 指向实际存储字符串的地址,也就是字节数组的头部
- 8~16 字节的 len 代表字符串长度
字符串拼接效率
Go 的 string 直接相加的操作效率较低
str3 = str1 + str2
每一次拼接都需要重新分配内存,首先分配一个 str3 所需要的内存,然后将 str1 与 str2 进行内存拷贝。如果构建个超大的字符串,那么这种性能是非常低的。比如这个for循环拼接:
var str string
for i := 0; i < 1000; i++ {
str += "zhongger"
}
每执行一次加号拼接,就会重新分配内存和进行内存拷贝。我们可以使用 Benchmark 来测试下性能
func StringAdd() string {
var str string
for i := 0; i < 1000; i++ {
str += "zhongger"
}
return str
}
func BenchmarkTestString(b *testing.B) {
for i := 0; i < b.N; i++ {
StringAdd()
}
}
使用命令 go test -bench=. -benchmem 执行之,输出以下结果
由 999 allocs/op 可以知道,StringAdd 方式的字符串拼接,进行了 999 次的内存分配申请。那么我们有没有办法进行优化呢?当然有,接下来介绍下 strings.Join 方法
strings.Join 方法字符串拼接
func StringJoin() string {
list := make([]string, 1000)
for i := 0; i < 1000; i++ {
list[i] = "zhongger"
}
return strings.Join(list, "")
}
func BenchmarkTestJoin(b *testing.B) {
for i := 0; i < b.N; i++ {
StringJoin()
}
}
使用命令 go test -bench=. -benchmem 执行之,输出以下结果
由 1 allocs/op 可以知道,使用 strings.Join 方法进行字符串拼接,只进行了 1 次内存分配申请,性能得到了非常大的提高。
小结
今天主要学习了 string 的高性能字符串拼接方法 strings.Join ,明天会有更多的字符串拼接性能优化的原理分析,大家敬请期待