Jinyun's Notes 🚀

没什么天赋,爱好也不多,但愿坚持做些喜欢的事情

0%

Go 连接字符串

🍹 Go 中连接字符串的方法有很多种,其实掌握一两种较为高效的方法就足够了,其它权当看热闹。

几种连接方式

Concat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package hello

import "testing"

/*
=== RUN TestStringConcat
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestStringConcat (0.00s)
PASS
ok example.com/hello 0.014s
*/
func TestStringConcat(t *testing.T) {
s := "Hello "
s += "World!"
t.Log(s)
}

Sprintf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello

import (
"fmt"
"testing"
)

/*
=== RUN TestStringSprintf
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringSprintf (0.00s)
PASS
ok example.com/hello 0.010s
*/
func TestStringSprintf(t *testing.T) {
s := "Hello "
s = fmt.Sprintf("%s%s", s, "World!")
t.Log(s)
}

Join

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello

import (
"strings"
"testing"
)

/*
=== RUN TestStringJoin
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringJoin (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestStringJoin(t *testing.T) {
s := "Hello "
s = strings.Join([]string{s, "World!"}, "")
t.Log(s)
}

Buffer Write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package hello

import (
"bytes"
"testing"
)

/*
=== RUN TestBufferWrite
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestBufferWrite (0.00s)
PASS
ok example.com/hello 0.010s
*/
func TestBufferWrite(t *testing.T) {
buf := new(bytes.Buffer)
buf.WriteString("Hello ")
buf.WriteString("World!")
s := buf.String()
t.Log(s)
}

Bytes Append

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package hello

import "testing"

/*
=== RUN TestBytesAppend
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello
--- PASS: TestBytesAppend (0.00s)
PASS
ok example.com/hello
*/
func TestBytesAppend(t *testing.T) {
var b []byte
s := "Hello "
b = append(b, "World!"...)
t.Log(string(s))
}

Copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package hello

import "testing"

/*
=== RUN TestStringCopy
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringCopy (0.00s)
PASS
ok example.com/hello (cached)
*/
func TestStringCopy(t *testing.T) {
b := make([]byte, 0)
b = mycopy(b, "Hello ")
b = mycopy(b, "World!")
t.Log(string(b))
}

func mycopy(b []byte, s string) []byte {
n := len(b)
if n+len(s) > cap(b) {
t := make([]byte, 2*cap(b)+len(s))
copy(t, b)
b = t
}
b = b[0 : n+len(s)]
copy(b[n:], s)
return b
}

String Builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package hello

import (
"strings"
"testing"
)

/*
=== RUN TestStringBuilder
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestStringBuilder (0.00s)
PASS
ok example.com/hello 0.013s
*/
func TestStringBuilder(t *testing.T) {
var builder strings.Builder
builder.WriteString("Hello ")
builder.WriteString("World!")
s := builder.String()
t.Log(s)
}

性能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package hello

import (
"bytes"
"fmt"
"strings"
"testing"
)

const (
src = "Hello World!"
cnt = 10000
)

var expected = strings.Repeat(src, cnt)

func mycopy(b []byte, s string) []byte {
n := len(b)
if n+len(s) > cap(b) {
t := make([]byte, 2*cap(b)+len(s))
copy(t, b)
b = t
}
b = b[0 : n+len(s)]
copy(b[n:], s)
return b
}

func BenchmarkStringConcat(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < cnt; i++ {
str += src
}
res = str
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkStringSprintf(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < cnt; i++ {
str = fmt.Sprintf("%s%s", str, src)
}
res = str
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkStringJoin(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < cnt; i++ {
str = strings.Join([]string{str, src}, "")
}
res = str
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", res, expected)
}
}

func BenchmarkBufferWrite(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
buf := new(bytes.Buffer)
for i := 0; i < cnt; i++ {
buf.WriteString(src)
}
res = buf.String()
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkBytesAppend(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
var bytes []byte
for i := 0; i < cnt; i++ {
bytes = append(bytes, src...)
}
res = string(bytes)
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkStringCopy(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
for i := 0; i < cnt; i++ {
data = mycopy(data, src)
}
res = string(data)
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkStringCopy2(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
b := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
for i := 0; i < cnt; i++ {
l := len(b)
if l+len(src) > cap(b) {
t := make([]byte, 2*cap(b)+len(src))
copy(t, b)
b = t
}
b = b[0 : l+len(src)]
copy(b[l:], src)
}
res = string(b)
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

func BenchmarkStringBuilder(b *testing.B) {
var res string
for n := 0; n < b.N; n++ {
var builder strings.Builder
for i := 0; i < cnt; i++ {
builder.WriteString(src)
}
res = builder.String()
}
b.StopTimer()
if res != expected {
b.Errorf("got=%s, want=%s", string(res), expected)
}
}

在我本机的测试结果如下,对于这个测试结果我表示也没想到,但是我已经知道以后该怎么做了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ go test -v -benchmem -run=^$ -bench '^(BenchmarkStringConcat|BenchmarkStringSprintf|BenchmarkStringJoin|BenchmarkBufferWrite|BenchmarkBytesAppend|BenchmarkStringCopy|BenchmarkStringCopy2|BenchmarkStringBuilder)$' example.com/hello
goos: darwin
goarch: amd64
pkg: example.com/hello
cpu: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
BenchmarkStringConcat
BenchmarkStringConcat-4 8 131090517 ns/op 632845121 B/op 10004 allocs/op
BenchmarkStringSprintf
BenchmarkStringSprintf-4 4 295268326 ns/op 1075561174 B/op 29686 allocs/op
BenchmarkStringJoin
BenchmarkStringJoin-4 6 287073860 ns/op 632845282 B/op 10008 allocs/op
BenchmarkBufferWrite
BenchmarkBufferWrite-4 5631 210591 ns/op 441616 B/op 13 allocs/op
BenchmarkBytesAppend
BenchmarkBytesAppend-4 7819 152409 ns/op 628720 B/op 25 allocs/op
BenchmarkStringCopy
BenchmarkStringCopy-4 8820 118761 ns/op 441552 B/op 12 allocs/op
BenchmarkStringCopy2
BenchmarkStringCopy2-4 9598 135427 ns/op 441552 B/op 12 allocs/op
BenchmarkStringBuilder
BenchmarkStringBuilder-4 7729 156815 ns/op 505840 B/op 24 allocs/op
PASS
ok example.com/hello 17.512s
本笔记是笔者在学习和工作中的一些整理,如对您有用,请鼓励我继续写作