Golang 字符串拼接

字符串拼接应该在编程过程中比较常用的操作了,在Go语言中对字符串的拼接有多种处理方式,以下通过实例来一一讲解

1, +号拼接

这种应该是最直接最简单的方式了。

func StringPlus() string{
    var s string
    s ="社会主义核心价值观的基本内容:"
    s +="富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;"
    s +="自由、平等、公正、法治,是对美好社会的生动表述;"
    s +="爱国、敬业、诚信、友善”,是公民基本道德规范。"
    return s
}

运行go test -bench=. -benchmem 查看性能输出如下: BenchmarkStringPlus-85000000251ns/op640B/op3allocs/op

2,fmt拼接

func StringFmt() string {
    return fmt.Sprint ("社会主义核心价值观的基本内容:",
        "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;,",
        "自由、平等、公正、法治,是对美好社会的生动表述;",
        "爱国、敬业、诚信、友善”,是公民基本道德规范。")
}

BenchmarkStringPlus-810000000234ns/op288B/op1allocs/op

3,Join拼接

这个是利用strings.Join函数进行拼接,接受一个字符串数组,转换为一个拼接好的字符串。

func StringJoin () string {
    s := [] string {"社会主义核心价值观的基本内容:",
     "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;",
    "自由、平等、公正、法治,是对美好社会的生动表述;",
    "爱国、敬业、诚信、友善”,是公民基本道德规范。"}
    return strings.Join(s , ",")
}

BenchmarkStringPlus-810000000189ns/op576B/op2allocs/op

4,buffer

func  StringBuffer() string {
    var  b bytes.Buffer
    b.WriteString("社会主义核心价值观的基本内容:")
    b.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
    b.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
    b.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")
    return b.String()
}

BenchmarkStringPlus-83000000505ns/op1136B/op4allocs/op

5,builder

func StringBuilder () string {
    var s strings.Builder
    s.WriteString("社会主义核心价值观的基本内容:")
    s.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
    s.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
    s.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")
    return s.String()
}

BenchmarkStringBuffer10-810000000200ns/op480B/op3allocs/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.yuanmaketang.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()
}
ph.w
ph.w 8个月前

已被 0 人挖起

回应
登录 后发表评论
  • 消灭零回应