Golang 字符串拼接及builder優化


Overview

  • +號拼接
  • fmt拼接
  • Join拼接
  • buffer拼接
  • builder拼接

在少數據的情況下,這幾個方法相差不大,但是當要拼接的字符串很多的時候,推薦使用builder。而+號連接適用於短小的,常量字符串的拼接,因為編譯器會優化

+號拼接

s := s1+s2+s3

fmt拼接

s := fmt.Sprintf("%v%v",s1,s2)

Join拼接

接收一個字符串數組,轉換為一個拼接好的字符串

sList := []string{s1,s2}
s := strings.Join(SList,"") 

buffer拼接

不止可以拼接字符串,還可以拼接byte等

var b = bytes.Buffer
b.WriteString(s1)
b.WriteString(S2)
s := String() 

builder拼接

var b strings.Builder
b.WriteString(s1)
b.WriteString(s2)
s := b.String()

對builder進行優化

測試代碼

project\stringBuilder\sb.go

package stringBuilder

import "strings"

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()
}


project\stringBuilder\sb_test.go

package stringBuilder

import "testing"

const TESTSTRING  = "test,"

/*
	初始化函數
	生成一個具有N個字符串的數組
*/

func initStrings(N int) []string{
	s:=make([]string,N)
	for i:=0;i<N;i++{
		s[i]=TESTSTRING
	}
	return s;
}

/*
	測試
	10個字符串
*/
func BenchmarkStringBuilder10(b *testing.B) {
	p:= initStrings(10)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}

/*
	測試
	100個字符串
*/
func BenchmarkStringBuilder100(b *testing.B) {
	p:= initStrings(100)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}
/*
	測試
	1000個字符串
*/
func BenchmarkStringBuilder1000(b *testing.B) {
	p:= initStrings(1000)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}


測試結果

goos: windows
goarch: amd64
pkg: TestProject/stringBuilder
BenchmarkStringBuilder10-4   5163981    228 ns/op      120 B/op       4 allocs/op
BenchmarkStringBuilder100-4  1000000    1150 ns/op     1016 B/op      7 allocs/op
BenchmarkStringBuilder1000-4 107428     11735 ns/op    21240 B/op     13 allocs/op
PASS

builder慢在哪了?

從基准測試結果可以看出,主要是慢在了多次內存分配上,如果多次內存分配而且引起GC的話,就會更慢!

builder 源碼

// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *Builder) WriteString(s string) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, s...)
	return len(s), nil
}

簡單來說,就是通過append函數向[]byte b中填充,當b字節數組擴容時,就會引起內存分配,從而使得操作變慢。

解決辦法

減少內存分配次數,也就是預先給b字節數組分配大小cap

修改代碼

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()
}
//測試代碼以10個字符串為例
//修改為如下
func BenchmarkStringBuilder10(b *testing.B) {
	p:= initStrings(10)
	b.ResetTimer()
	cap := 10*len(TESTSTRING)
	for i:=0;i<b.N;i++{
		StringBuilder(p,cap)
	}
}

優化后測試結果

BenchmarkStringBuilder10-4   10027047    114 ns/op    64 B/op    1 allocs/op
BenchmarkStringBuilder100-4  1312066     810 ns/op    512 B/op   1 allocs/op
BenchmarkStringBuilder1000-4  141570     8080 ns/op   5376 B/op   1 allocs/op
PASS

能優化20%~50%


免責聲明!

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



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