Overview
- +号拼接
- fmt拼接
- Join拼接
- buffer拼接
- builder拼接
在少数据的情况下,这几个方法相差不大,但是当要拼接的字符串很多的时候,推荐使用builder
。而+号连接适用于短小的,常量字符串的拼接,因为编译器会优化
+号拼接
s := s1+s2+s3
fmt拼接
s := fmt.Sprintf("%v%v",s1,s2)
Join拼接
接收一个字符串数组,转换为一个拼接好的字符串
sList := []string{s1,s2}
s := strings.Join(SList,"")
buffer拼接
不止可以拼接字符串,还可以拼接byte等
var b = bytes.Buffer
b.WriteString(s1)
b.WriteString(S2)
s := String()
builder拼接
var b strings.Builder
b.WriteString(s1)
b.WriteString(s2)
s := b.String()
对builder进行优化
测试代码
projectstringBuildersb.go
package stringBuilder
import "strings"
func StringBuilder(p []string) string {
var b strings.Builder
l:=len(p)
for i:=0;i<l;i++{
b.WriteString(p[i])
}
return b.String()
}
projectstringBuildersb_test.go
package stringBuilder
import "testing"
const TESTSTRING = "test,"
/*
初始化函数
生成一个具有N个字符串的数组
*/
func initStrings(N int) []string{
s:=make([]string,N)
for i:=0;i<N;i++{
s[i]=TESTSTRING
}
return s;
}
/*
测试
10个字符串
*/
func BenchmarkStringBuilder10(b *testing.B) {
p:= initStrings(10)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
/*
测试
100个字符串
*/
func BenchmarkStringBuilder100(b *testing.B) {
p:= initStrings(100)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
/*
测试
1000个字符串
*/
func BenchmarkStringBuilder1000(b *testing.B) {
p:= initStrings(1000)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
测试结果
goos: windows
goarch: amd64
pkg: TestProject/stringBuilder
BenchmarkStringBuilder10-4 5163981 228 ns/op 120 B/op 4 allocs/op
BenchmarkStringBuilder100-4 1000000 1150 ns/op 1016 B/op 7 allocs/op
BenchmarkStringBuilder1000-4 107428 11735 ns/op 21240 B/op 13 allocs/op
PASS
builder
慢在哪了?
从基准测试结果可以看出,主要是慢在了多次内存分配上,如果多次内存分配而且引起GC的话,就会更慢!
builder
源码
// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}
简单来说,就是通过append
函数向[]byte b
中填充,当b
字节数组扩容时,就会引起内存分配,从而使得操作变慢。
解决办法
减少内存分配次数,也就是预先给b字节数组
分配大小cap
修改代码
func StringBuilder(p []string,cap int) string {
var b strings.Builder
l:=len(p)
b.Grow(cap)
for i:=0;i<l;i++{
b.WriteString(p[i])
}
return b.String()
}
//测试代码以10个字符串为例
//修改为如下
func BenchmarkStringBuilder10(b *testing.B) {
p:= initStrings(10)
b.ResetTimer()
cap := 10*len(TESTSTRING)
for i:=0;i<b.N;i++{
StringBuilder(p,cap)
}
}
优化后测试结果
BenchmarkStringBuilder10-4 10027047 114 ns/op 64 B/op 1 allocs/op
BenchmarkStringBuilder100-4 1312066 810 ns/op 512 B/op 1 allocs/op
BenchmarkStringBuilder1000-4 141570 8080 ns/op 5376 B/op 1 allocs/op
PASS
能优化20%
~50%