golang的字符串拼接


常用拼接方法

字符串拼接在日常開發中是很常見的需求,目前有兩種普遍做法:

一種是直接用 += 來拼接

s1 := "Hello"
s2 := "World"
s3 := s1 + s2  // s3 == "HelloWorld"
s1 += s2       // s1 == "HelloWorld"

這是最常用也是最簡單直觀的方法,不過簡單是有代價的,golang的字符串是不可變類型,也就是說每一次對字符串的“原地”修改都會重新生成一個string,再把數據復制進去,這樣一來將會產生很可觀的性能開銷,稍后的性能測試中將會看到這一點。

 

第二種是使用bytes.Buffer

// bytes.Buffer的0值可以直接使用
var buff bytes.Buffer

// 向buff中寫入字符/字符串
buff.Write([]byte("Hello"))
buff.WriteByte(' ')
buff.WriteString("World")

// String() 方法獲得拼接的字符串
buff.String() // "Hello World"

這種方法用於需要大量進行字符串拼接操作的場合,性能要大大優於第一種方法。

 

不過使用bytes模塊來操作string難免讓人產生迷惑,所以在go1.10中新增了第三種方法:strings.Builder,官方鼓勵盡量在string的拼接時使用Builder,byte拼接時使用Buffer

// strings.Builder的0值可以直接使用
var builder strings.Builder

// 向builder中寫入字符/字符串
builder.Write([]byte("Hello"))
builder.WriteByte(' ')
builder.WriteString("World")

// String() 方法獲得拼接的字符串
builder.String() // "Hello World"

從上面的代碼中可以看到,strings.Builder和bytes.Buffer的操作幾乎一樣,不過strings.Builder僅僅實現了write類方法,而Buffer是可讀可寫的。

所以strings.Builder僅用於拼接/構建字符串

性能

除了是否易用外,另一條參考標准就是性能,得益於golang自帶的測試工具,我們可以大致對比一下三種方案的性能。

測試使用從26個大寫和小寫字母10個數字以及5個常用符號共67字符中隨機取10個組成string或[]byte,再由Buffer和Builder進行拼接。

先上測試結果

go test -bench=. -benchmem

 

下面是測試代碼

// BenchmarkSpliceAddString10 測試使用 += 拼接N次長度為10的字符串
func BenchmarkSpliceAddString10(b *testing.B) {
    s := ""
    for i := 0; i < b.N; i++ {
        s += GenRandString(10)
    }
}

// BenchmarkSpliceBuilderString10 測試使用strings.Builder拼接N次長度為10的字符串
func BenchmarkSpliceBuilderString10(b *testing.B) {
    var builder strings.Builder
    for i := 0; i < b.N; i++ {
        builder.WriteString(GenRandString(10))
    }
}

// BenchmarkSpliceBufferString10 測試使用bytes.Buffer拼接N次長度為10的字符串
func BenchmarkSpliceBufferString10(b *testing.B) {
    var buff bytes.Buffer
    for i := 0; i < b.N; i++ {
        buff.WriteString(GenRandString(10))
    }
}

// BenchmarkSpliceBufferByte10 測試使用bytes.Buffer拼接N次長度為10的[]byte
func BenchmarkSpliceBufferByte10(b *testing.B) {
    var buff bytes.Buffer
    for i := 0; i < b.N; i++ {
        buff.Write(GenRandBytes(10))
    }
}

// BenchmarkSpliceBuilderByte10 測試使用string.Builder拼接N次長度為10的[]byte
func BenchmarkSpliceBuilderByte10(b *testing.B) {
    var builder strings.Builder
    for i := 0; i < b.N; i++ {
        builder.Write(GenRandBytes(10))
    }
}

這是生成供拼接使用的隨機字符串的代碼(這里仍然使用了bytes.Buffer,推薦使用新的strings.Builder)

const (
    data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890,.-=/"
)

func init() {
    rand.Seed(time.Now().Unix()) // 設置隨機種子
}

// GenRandString 生成n個隨機字符的string
func GenRandString(n int) string {
    max := len(data)
    var buf bytes.Buffer
    for i := 0; i < n; i++ {
        buf.WriteByte(data[rand.Intn(max)])
    }

    return buf.String()
}

// GenRandBytes 生成n個隨機字符的[]byte
func GenRandBytes(n int) []byte {
    max := len(data)
    buf := make([]byte, n)
    for i := 0; i < n; i++ {
        buf[i] = data[rand.Intn(max)]
    }

    return buf
}

 

使用 += 的方法性能是最慢的,性能和其他兩種差了好幾個數量級。

Buffer和Builder性能相差無幾,Builder在內存的使用上要略優於Buffer

結論

strings.Builder在golang 1.10才引入標准庫的,所以 version <= 1.9 的時候對於大量字符串的拼接操作推薦bytes.Buffer

如果你正在使用1.10+,那么建議使用strings.Builder,不僅是更好的性能,也是為了能使代碼更清晰。

當然,對於簡單的拼接,+= 就足夠了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM