GolangPHP程序员技术栈

Golang 字符串拼接

2019-04-03  本文已影响3人  Gundy_

字符串拼接应该在编程过程中比较常用的操作了,在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()
}

本文亦在微信公众号【小道资讯】发布,欢迎扫码关注!


image
上一篇下一篇

猜你喜欢

热点阅读