总结go语言中五种字符串的拼接方式以及性能比较

561 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

操作字符串离不开字符串的拼接,字符串拼接在go里面有很多种实现,但是Go中string是只读类型,大量字符串的拼接会造成性能问题。本文主要介绍一下go语言中常用的五种字符串拼接方式以及性能,感兴趣的小伙伴可以参考一下。

+拼接方式

php使用.拼接,go语言中用+拼接,这种方式是最常用的也是最简单的,不过由于golang中字符串是不可变得类型,因此用+拼接会产生一个新的字符串对效率有影响。

func main(){
    s1 := "good"
    s2 := "morning"
    s3 := s1 + " " +s2
    fmt.Println(s3) // good morning
}

fmt.Sprintf函数

这种方式也是开发过程中经常使用到的,这种写的好处是不会直接产生临时字符串,但是效率也不是特别高。

s1 := "good"
s2 := "morning"
s3 := fmt.Sprintf("%s %s", s1, s2)
fmt.Println(s3) // good morning

strings.Join函数

使用Join函数我们需要先引入strings包才可以,Join函数会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,如果没有的话效率也不高,一般常用于切片转为字符串。

s1 := "good"
s2 := "morning"
str := []string{s1, s2}
s3 := strings.Join(str, "")
fmt.Println(s3) // good morning

buffer.Builderbuffer.WriteString函数

因为string类型底层就是一个byte数组,所以我们就可以Go语言的bytes.Buffer进行字符串拼接。

bytes.buffer是一个缓冲byte类型的缓冲器,存放着都是byte,是一个变长的 buffer,具有 Read 和Write 方法。 buffer 的 零值 是一个 空的 buffer,但是可以使用,底层就是一个 []byte, 字节切片。

效率比上面的高不少但是我在开发中基本上没有用过。

s1 := "good"
s2 := "morning"
var buf bytes.Buffer
buf.WriteString(s1)
buf.WriteString(s2)
s3 := buf.String()
fmt.Println(s3) // goodmorning

向Buffer中写数据,可以看出Buffer中有个Grow函数用于对切片进行扩容。

func (b *Buffer) WriteString(s string) (n int, err error) {
	b.lastRead = opInvalid
	m, ok := b.tryGrowByReslice(len(s))
	if !ok {
		m = b.grow(len(s))
	}
	return copy(b.buf[m:], s), nil
}

buffer.Builder函数

官方建议使用的的拼接方式,string.Builder 通过使用一个内部的 slice 来存储数据片段。当开发者调用写入方法的时候,数据实际上是被追加(append)到了其内部的 slice 上。

func (b *Builder) WriteString(s string) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, s...)
	return len(s), nil
}

示例:

s1 := "good"
s2 := "morning"
var build strings.Builder
build.WriteString(s1)
build.WriteString(s2)
s3 := build.String()
fmt.Println(s3) // goodmorning

bytes.Buffer 是重新申请了一块空间,存放生成的string变量, 而strings.Builder直接将底层的[]byte转换成了string类型返回了回来,去掉了申请空间的操作。所以strings.Builder效率比bytes.Buffer高。

总结

  • 性能要求不太高的场景,直接使用运算符+,代码更简短清晰,有较好的可读性
  • 如果需要拼接的不仅仅是字符串,还有数字之类的其他需求的话,可以用fmt.Sprintf()
  • 在已有字符串数组的场合,使用 strings.Join() 有比较好的性能
  • 在一些性能要求较高的场合,尽量使用 buffer.WriteString() 以获得更好的性能