淺析Golang中字符串拼接問題
1.概述
Go的字符串是一個(gè)不可改變的數(shù)據(jù)結(jié)構(gòu),這和其他語言如JAVA,C++等的設(shè)定很類似.總體來說,有如下五種拼接方式,下面我們將論述各種方式的性能問題,以及如何選擇.

(golang字符串,內(nèi)存模型)
type StringHeader struct {
Data uintptr
Len int
}
注意:字符串具有不可改變的特性,即便通過指針等變相操作
var a string = "old"
bptr := (*reflect.StringHeader)(unsafe.Pointer(&a))
dataPtr := (*byte)(unsafe.Pointer(bptr.Data))
var b = [3]byte{'n', 'e', 'w'}
*dataPtr = b[0] //報(bào)錯(cuò)
fmt.Println(bptr)
2.Golang中字符串拼接的方式
方式一、直接+
當(dāng)使用連接符 + 拼接兩個(gè)字符串時(shí),會(huì)生成一個(gè)新的字符串并開辟新的內(nèi)存空間,空間大小等于兩個(gè)字符串之和。在訓(xùn)中中時(shí),不斷拼接新的字符串,這樣就會(huì)不斷申請(qǐng)內(nèi)存空間, 性能就會(huì)越來越差。 所以,在字符串密集拼接場(chǎng)景中,使用 + 會(huì)嚴(yán)重降低性能。包括熱路徑的代碼.
方式二、strings.Builder
func Benchmark_StringsBuilder(b *testing.B) {
var sb strings.Builder
for i := 0; i < b.N; i++ {
sb.WriteString("hello world")
}
_ = sb.String()
}
方式三、bytes.Buffer
func Benchmark_BytesBuffer(b *testing.B) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.WriteString("hello world")
}
_ = buf.String()
}
方式四、fmt.Fprint(&buf,&str)
方式五、strings.Join
性能不是最優(yōu),但在切片的情況下,可以用來拼接
3.總結(jié)
Benchmark_StringAdd Benchmark_StringAdd-8 117806 127059 ns/op Benchmark_BytesBuffer Benchmark_BytesBuffer-8 38938282 25.88 ns/op Benchmark_StringsBuilder Benchmark_StringsBuilder-8 57249450 18.53 ns/op
3.1 性能方面,strings.Builder 比 bytes.Buffer 快差不多 20%,
原因:strings.Builder 和 bytes.Buffer 底層都是一個(gè) []byte,但是 bytes.Buffer 轉(zhuǎn)換字符串時(shí)會(huì)重新申請(qǐng)內(nèi)存空間用來存放, 而 strings.Builder 直接將底層的 []byte 利用指針的方式強(qiáng)轉(zhuǎn)為字符串.
//strings.Builder的String()
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
//bytes.Builder的String()
func (b *Buffer) String() string {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
return string(b.buf[b.off:])
}3.2 strings.Builder通常性能最優(yōu),但底層依賴于[]byte,所以如果平凡擴(kuò)容就不妙了,因此我們需要借助它的Grow方法,以已分配最終[]byte的容量,避免因?yàn)閿U(kuò)容帶來的性能損失
func Benchmark_StringConcat(b *testing.B) {
str := "hello world"
var sb strings.Builder
sb.Grow(b.N * len(str))
for i := 0; i < b.N; i++ {
sb.WriteString(str)
}
_ = sb.String()
}3.3 strings.Builder沒有拷貝構(gòu)造(借用C++說法),因?yàn)?/p>
type Builder struct {
addr *Builder // of receiver, to detect copies by value
buf []byte //如果拷貝,這個(gè)buf共享,最后導(dǎo)致數(shù)據(jù)混亂
}到此這篇關(guān)于淺析Golang中字符串拼接問題的文章就介紹到這了,更多相關(guān)Golang字符串拼接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang結(jié)構(gòu)化日志包log/slog的使用詳解
官方提供的用于打印日志的包是標(biāo)準(zhǔn)庫中的 log 包,該包雖然被廣泛使用,但是缺點(diǎn)也很多,所以Go 1.21新增的 log/slog 完美解決了以上問題,下面我們就來看看log/slog包的使用吧2023-09-09
Golang中匿名組合實(shí)現(xiàn)偽繼承的方法
這篇文章主要介紹了Golang中匿名組合實(shí)現(xiàn)偽繼承的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
Go語言:打造優(yōu)雅數(shù)據(jù)庫單元測(cè)試的實(shí)戰(zhàn)指南
Go語言數(shù)據(jù)庫單元測(cè)試入門:聚焦高效、可靠的數(shù)據(jù)庫代碼驗(yàn)證!想要確保您的Go應(yīng)用數(shù)據(jù)層堅(jiān)如磐石嗎?本指南將手把手教您如何利用Go進(jìn)行數(shù)據(jù)庫單元測(cè)試,輕松揪出隱藏的bug,打造無懈可擊的數(shù)據(jù)處理邏輯,一起來探索吧!2024-01-01
Golang實(shí)現(xiàn)延遲調(diào)用的項(xiàng)目實(shí)踐
本文主要介紹了Golang實(shí)現(xiàn)延遲調(diào)用的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
jenkins配置golang?代碼工程自動(dòng)發(fā)布的實(shí)現(xiàn)方法
這篇文章主要介紹了jenkins配置golang?代碼工程自動(dòng)發(fā)布,jks是個(gè)很好的工具,使用方法也很多,我只用了它簡單的功能,對(duì)jenkins配置golang相關(guān)知識(shí)感興趣的朋友一起看看吧2022-07-07

