Go可以对字符串直接连接,但更有效的方法是是使用Builder结构,和java中StringBuffer类似。本文介绍如何通过strings.Builder高效构建字符串,通过示例介绍WriteString 和 WriteRune方法,并进行性能对比builder的优势。最后列出常用的字符串操作方法。
Builder介绍
Builder是Go内置结构体,源码如下:
// A Builder is used to efficiently build a string using Write methods.
// It minimizes memory copying. The zero value is ready to use.
// Do not copy a non-zero Builder.
type Builder struct {
addr *Builder // of receiver, to detect copies by value
buf []byte
}
其内部为字节数组,动态分配内存,但不是每次都分配:
// grow copies the buffer to a new, larger buffer so that there are at least n
// bytes of capacity beyond len(b.buf).
func (b *Builder) grow(n int) {
buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
copy(buf, b.buf)
b.buf = buf
}
// Grow grows b's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to b
// without another allocation. If n is negative, Grow panics.
func (b *Builder) Grow(n int) {
b.copyCheck()
if n < 0 {
panic("strings.Builder.Grow: negative count")
}
if cap(b.buf)-len(b.buf) < n {
b.grow(n)
}
}
WriteString方法
下面示例使用strings.Builder构建字符串:
package main
import (
"fmt"
"strings"
)
func main() {
builder := strings.Builder{}
builder.WriteString("There")
builder.WriteString(" are")
builder.WriteString(" three")
builder.WriteString(" hawks")
builder.WriteString(" in the sky")
fmt.Println(builder.String())
}
使用WriteString方法构建字符串,输出结果为:
go run simple.go
There are three hawks in the sky
Write方法
下面示例通过字节slice构建字符串:
package main
import (
"fmt"
"strings"
)
func main() {
builder := strings.Builder{}
data1 := []byte{72, 101, 108, 108, 111}
data2 := []byte{32}
data3 := []byte{116, 104, 101, 114, 101, 33}
builder.Write(data1)
builder.Write(data2)
builder.Write(data3)
fmt.Println(builder.String())
}
上面示例通过Write方法构建字符串,输出结果为:
go run simple2.go
Hello there!
另外还有 builder.WriteByte(),builder.WriteRune()方法,每次写一个字节。
性能对比
下面比较Builder与字符串连接的性能:
package main
import (
"fmt"
"strings"
"time"
)
func main() {
t0 := time.Now()
builder := strings.Builder{}
for i := 0; i < 100_000; i++ {
builder.WriteString("falcon")
}
t1 := time.Now()
result := ""
for i := 0; i < 100_000; i++ {
result += "falcon"
}
t2 := time.Now()
fmt.Println(t1.Sub(t0))
fmt.Println(t2.Sub(t1))
}
上面示例对比两种方法,分别连接字符串10万次。运行结果为:
1.0285ms
2.8703108s
其他字符串连接方法
strings.Join
Join连接slice/array的元素:
package main
import (
"fmt"
"strings"
)
func main() {
words := []string{"an", "old", "falcon"}
msg := strings.Join(words, " ")
fmt.Println(msg)
}
fmt.Sprintf
使用格式连接字符串:
package main
import "fmt"
func main() {
w1 := "old"
w2 := "falcon"
msg := fmt.Sprintf("%s %s", w1, w2)
fmt.Println(msg)
}
bytes.Buffer
与strings.Builder原理一致,读者可以查看其源码:
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer
buf.WriteString("an ")
buf.WriteString("old ")
buf.WriteString("falcon")
fmt.Println(buf.String())
}
当然也可以slice实现:
package main
import (
"fmt"
)
func main() {
var s1 = "an"
var s2 = " old"
var s3 = " falcon"
msg := make([]byte, 0)
msg = append(msg, []byte(s1)...)
msg = append(msg, []byte(s2)...)
msg = append(msg, []byte(s3)...)
fmt.Println(string(msg))
}
加号操作符
最后还在提及下加号操作:
package main
import "fmt"
func main() {
w1 := "an"
w2 := " old"
w3 := " falcon"
msg := w1 + w2 + w3
fmt.Println(msg)
str := "There are"
str += " three falcons"
str += " in the sky"
fmt.Println(str)
}