字符串拼接应该在编程过程中比较常用的操作了,在Go语言中对字符串的拼接有多种处理方式,以下通过实例来一一讲解
+号拼接
这种应该是最直接最简单的方式了。
func StringPlus() string {
var s string
s = "社会主义核心价值观的基本内容:"
s += "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;"
s += "自由、平等、公正、法治,是对美好社会的生动表述;"
s += "爱国、敬业、诚信、友善”,是公民基本道德规范。"
return s
}
运行go test -bench=. -benchmem 查看性能输出如下:
BenchmarkStringPlus-8 5000000 251 ns/op 640 B/op 3 allocs/op
fmt拼接
func StringFmt() string {
return fmt.Sprint("社会主义核心价值观的基本内容:",
"富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;,",
"自由、平等、公正、法治,是对美好社会的生动表述;",
"爱国、敬业、诚信、友善”,是公民基本道德规范。")
}
BenchmarkStringPlus-8 10000000 234 ns/op 288 B/op 1 allocs/op
Join拼接
这个是利用strings.Join函数进行拼接,接受一个字符串数组,转换为一个拼接好的字符串。
func StringJoin() string {
s := []string{"社会主义核心价值观的基本内容:", "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;",
"自由、平等、公正、法治,是对美好社会的生动表述;",
"爱国、敬业、诚信、友善”,是公民基本道德规范。"}
return strings.Join(s, ",")
}
BenchmarkStringPlus-8 10000000 189 ns/op 576 B/op 2 allocs/op
buffer
func StringBuffer() string {
var b bytes.Buffer
b.WriteString("社会主义核心价值观的基本内容:")
b.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
b.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
b.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")
return b.String()
}
BenchmarkStringPlus-8 3000000 505 ns/op 1136 B/op 4 allocs/op
builder
func StringBuilder() string {
var s strings.Builder
s.WriteString("社会主义核心价值观的基本内容:")
s.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
s.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
s.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")
return s.String()
}
BenchmarkStringBuffer10-8 10000000 200 ns/op 480 B/op 3 allocs/op
以上是进行了五次字符串的拼接,可以看到buffer的性能较差一些,其他大致三种方式区别不大
那么100个字符串1000个字符串拼接又如何呢
package main
import (
"bytes"
"fmt"
"strings"
)
func StringPlus(p []string) string {
var s string
l := len(p)
for i := 0; i < l; i++ {
s += p[i]
}
return s
}
func StringFmt(p []interface{}) string {
return fmt.Sprint(p...)
}
func StringJoin(p []string) string {
return strings.Join(p, "")
}
func StringBuffer(p []string) string {
var b bytes.Buffer
l := len(p)
for i := 0; i < l; i++ {
b.WriteString(p[i])
}
return b.String()
}
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()
}
进行如下压测
package main
import "testing"
const WebSite = "https://www.china.com/"
const StringLen = 1000
func initStrings(N int) []string{
s:=make([]string,N)
for i:=0;i<N;i++{
s[i]=WebSite
}
return s
}
func initStringi(N int) []interface{}{
s:=make([]interface{},N)
for i:=0;i<N;i++{
s[i]=WebSite
}
return s
}
func BenchmarkStringPlus10(b *testing.B) {
p:= initStrings(StringLen)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringPlus(p)
}
}
func BenchmarkStringFmt10(b *testing.B) {
p:= initStringi(StringLen)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringFmt(p)
}
}
func BenchmarkStringJoin10(b *testing.B) {
p:= initStrings(StringLen)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringJoin(p)
}
}
func BenchmarkStringBuffer10(b *testing.B) {
p:= initStrings(StringLen)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuffer(p)
}
}
func BenchmarkStringBuilder10(b *testing.B) {
p:= initStrings(StringLen)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
压测结果如下:
BenchmarkStringPlus10-8 1000 1905639 ns/op 11573410 B/op 999 allocs/op
BenchmarkStringFmt10-8 50000 32464 ns/op 24586 B/op 1 allocs/op
BenchmarkStringJoin10-8 100000 17600 ns/op 49152 B/op 2 allocs/op
BenchmarkStringBuffer10-8 50000 27480 ns/op 122544 B/op 11 allocs/op
BenchmarkStringBuilder10-8 100000 20535 ns/op 96224 B/op 16 allocs/op
可以看到Join 和 builder表现最好。但是一般是有数组切片进行字符串拼接我们采用join, 如果没有的话还是builder更合适。
builder 优化
查看WriteString的源码我们可以发现,这里有对b.buf进行append操作,那对于长的字符串就会触发扩容操作影响性能
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}
由于扩容导致的问题,那我们是否可以事先分配好所需的容量呢,查看buIlder源码发现提供了一个Grow方法,正是来进行容量分配的。
func (b *Builder) grow(n int) {
buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
copy(buf, b.buf)
b.buf = buf
}
那我们你可以优化StringBuilder如下:
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()
}
本文亦在微信公众号【小道资讯】发布,欢迎扫码关注!
有疑问加站长微信联系(非本文作者)