GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解_第1頁
GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解_第2頁
GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解_第3頁
GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解_第4頁
GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第GoLangstrings.Builder底層實(shí)現(xiàn)方法詳解目錄1.strings.Builder結(jié)構(gòu)體1.1strings.Builder結(jié)構(gòu)體1.2Write方法1.3WriteByte方法1.4WriteRune方法1.5.WriteString方法1.6String方法1.7Len方法1.8Cap方法1.9Reset方法1.10Grow方法1.11grow方法1.12copyCheck方法2.strings.Builder介紹3.存儲原理4.拷貝問題5.不能與nil作比較6.Grow深入7.不支持并行讀寫

1.strings.Builder結(jié)構(gòu)體

1.1strings.Builder結(jié)構(gòu)體

//ABuilderisusedtoefficientlybuildastringusingWritemethods.

//Itminimizesmemorycopying.Thezerovalueisreadytouse.

//Donotcopyanon-zeroBuilder.

typeBuilderstruct{

addr*Builder//ofreceiver,todetectcopiesbyvalue

buf[]byte

1.2Write方法

//Writeappendsthecontentsofptob'sbuffer.

//Writealwaysreturnslen(p),nil.

func(b*Builder)Write(p[]byte)(int,error){

b.copyCheck()

b.buf=append(b.buf,p...)

returnlen(p),nil

1.3WriteByte方法

//WriteByteappendsthebytectob'sbuffer.

//Thereturnederrorisalwaysnil.

func(b*Builder)WriteByte(cbyte)error{

b.copyCheck()

b.buf=append(b.buf,c)

returnnil

1.4WriteRune方法

//WriteRuneappendstheUTF-8encodingofUnicodecodepointrtob'sbuffer.

//Itreturnsthelengthofrandanilerror.

func(b*Builder)WriteRune(rrune)(int,error){

b.copyCheck()

//Compareasuint32tocorrectlyhandlenegativerunes.

ifuint32(r)utf8.RuneSelf{

b.buf=append(b.buf,byte(r))

return1,nil

l:=len(b.buf)

ifcap(b.buf)-lutf8.UTFMax{

b.grow(utf8.UTFMax)

n:=utf8.EncodeRune(b.buf[l:l+utf8.UTFMax],r)

b.buf=b.buf[:l+n]

returnn,nil

1.5.WriteString方法

//WriteStringappendsthecontentsofstob'sbuffer.

//Itreturnsthelengthofsandanilerror.

func(b*Builder)WriteString(sstring)(int,error){

b.copyCheck()

b.buf=append(b.buf,s...)

returnlen(s),nil

1.6String方法

和bytes.Buffer一樣,strings.Builder也支持使用String()來獲取最終的字符串結(jié)果。為了節(jié)省內(nèi)存分配,它通過使用指針技術(shù)將內(nèi)部的bufferbytes轉(zhuǎn)換為字符串。所以String()方法在轉(zhuǎn)換的時(shí)候節(jié)省了時(shí)間和空間。

//Stringreturnstheaccumulatedstring.

func(b*Builder)String()string{

return*(*string)(unsafe.Pointer(b.buf))

1.7Len方法

//Lenreturnsthenumberofaccumulatedbytes;b.Len()==len(b.String()).

func(b*Builder)Len()int{returnlen(b.buf)}

1.8Cap方法

//Capreturnsthecapacityofthebuilder'sunderlyingbyteslice.Itisthe

//totalspaceallocatedforthestringbeingbuiltandincludesanybytes

//alreadywritten.

func(b*Builder)Cap()int{returncap(b.buf)}

1.9Reset方法

//ResetresetstheBuildertobeempty.

func(b*Builder)Reset(){

b.addr=nil

b.buf=nil

1.10Grow方法

//Growgrowsb'scapacity,ifnecessary,toguaranteespacefor

//anothernbytes.AfterGrow(n),atleastnbytescanbewrittentob

//withoutanotherallocation.Ifnisnegative,Growpanics.

func(b*Builder)Grow(nint){

b.copyCheck()

ifn0{

panic("strings.Builder.Grow:negativecount")

ifcap(b.buf)-len(b.buf)n{

b.grow(n)

1.11grow方法

//growcopiesthebuffertoanew,largerbuffersothatthereareatleastn

//bytesofcapacitybeyondlen(b.buf).

func(b*Builder)grow(nint){

buf:=make([]byte,len(b.buf),2*cap(b.buf)+n)

copy(buf,b.buf)

b.buf=buf

1.12copyCheck方法

func(b*Builder)copyCheck(){

ifb.addr==nil{

//ThishackworksaroundafailingofGo'sescapeanalysis

//thatwascausingbtoescapeandbeheapallocated.

//Seeissue23382.

//TODO:onceissue7921isfixed,thisshouldberevertedto

//just"b.addr=b".

b.addr=(*Builder)(noescape(unsafe.Pointer(b)))

}elseifb.addr!=b{

panic("strings:illegaluseofnon-zeroBuildercopiedbyvalue")

2.strings.Builder介紹

與bytes.Buffer類似,strings.Builder也支持4類方法將數(shù)據(jù)寫入builder中。

func(b*Builder)Write(p[]byte)(int,error)

func(b*Builder)WriteByte(cbyte)error

func(b*Builder)WriteRune(rrune)(int,error)

func(b*Builder)WriteString(sstring)(int,error)

有了它們,用戶可以根據(jù)輸入數(shù)據(jù)的不同類型(byte數(shù)組,byte,rune或者string),選擇對應(yīng)的寫入方法。

3.存儲原理

根據(jù)用法說明,我們通過調(diào)用string.Builder的寫入方法來寫入內(nèi)容,然后通過調(diào)用String()方法來獲取拼接的字符串。那么string.Builder是如何組織這些內(nèi)容的呢?

通過slice,string.Builder通過使用一個(gè)內(nèi)部的slice來存儲數(shù)據(jù)片段。當(dāng)開發(fā)者調(diào)用寫入方法的時(shí)候,數(shù)據(jù)實(shí)際上是被追加(append)到了其內(nèi)部的slice上。

4.拷貝問題

strings.Builder不推薦被拷貝。當(dāng)你試圖拷貝strings.Builder并寫入的時(shí)候,你的程序就會崩潰。

你已經(jīng)知道,strings.Builder內(nèi)部通過slice來保存和管理內(nèi)容。slice內(nèi)部則是通過一個(gè)指針指向?qū)嶋H保存內(nèi)容的數(shù)組。當(dāng)我們拷貝了builder以后,同樣也拷貝了其slice的指針。但是它仍然指向同一個(gè)舊的數(shù)組。當(dāng)你對源builder或者拷貝后的builder寫入的時(shí)候,問題就產(chǎn)生了。另一個(gè)builder指向的數(shù)組內(nèi)容也被改變了。這就是為什么strings.Builder不允許拷貝的原因。

funcmain(){

varb1strings.Builder

b1.WriteString("ABC")

b2:=b1

b2.WriteString("DEF")//出錯(cuò)在這一行,panic:strings:illegaluseofnon-zeroBuildercopiedbyvalue

funcmain(){

varb1strings.Builder

b1.WriteString("ABC")

b2:=b1

fmt.Println(b2.String())//ABC

funcmain(){

varb1strings.Builder

b1.WriteString("ABC")

b2:=b1

fmt.Println(b1.String())//輸出:ABC

fmt.Println(b2.String())//輸出:ABC

b1.WriteString("DEF")

fmt.Println(b1.String())//輸出:ABCDEF

fmt.Println(b2.String())//輸出:ABC

但對于一個(gè)未寫入任何東西的空內(nèi)容builder則是個(gè)例外。我們可以拷貝空內(nèi)容的builder而不報(bào)錯(cuò)。

funcmain(){

varb1strings.Builder

b2:=b1

fmt.Println(b1.String())//輸出空行

fmt.Println(b2.String())//輸出空行

b2.WriteString("DEF")

fmt.Println(b1.String())//輸出空行

fmt.Println(b2.String())//輸出:DEF

b1.WriteString("ABC")

fmt.Println(b1.String())//輸出:ABC

fmt.Println(b2.String())//輸出:DEF

strings.Builder會在以下方法中檢測拷貝操作:

Grow(nint)

Write(p[]byte)

WriteRune(rrune)

WriteString(sstring)

所以,拷貝并使用下列這些方法是允許的:

funcmain(){

//Reset()

//Len()

//String()

varb1strings.Builder

b1.WriteString("ABC")

b2:=b1

fmt.Println(b2.Len())//3

fmt.Println(b2.String())//ABC

b2.Reset()

b2.WriteString("DEF")

fmt.Println(b2.String())//DEF

5.不能與nil作比較

6.Grow深入

strings.Builder是通過其內(nèi)部的slice來儲存內(nèi)容的。當(dāng)你調(diào)用寫入方法的時(shí)候,新的字節(jié)數(shù)據(jù)就被追加到slice上。如果達(dá)到了slice的容量(capacity)限制,一個(gè)新的slice就會被分配,然后老的slice上的內(nèi)容會被拷貝到新的slice上。當(dāng)slice長度很大時(shí),這個(gè)操作就會很消耗資源甚至引起內(nèi)存問題。我們需要避免這一情況。

關(guān)于slice,Go語言提供了make([]TypeOfSlice,length,capacity)方法在初始化的時(shí)候預(yù)定義它的容量。這就避免了因達(dá)到最大容量而引起擴(kuò)容。

strings.Builder同樣也提供了Grow()來支持預(yù)定義容量。當(dāng)我們可以預(yù)定義我們需要使用的容量時(shí),strings.Builder就能避免擴(kuò)容而創(chuàng)建新的slice了。

當(dāng)調(diào)用Grow()時(shí),我們必須定義要擴(kuò)容的字節(jié)數(shù)(n)。Grow()方法保證了其內(nèi)部的slice一定能夠?qū)懭雗個(gè)字節(jié)。只有當(dāng)slice空余空間不足以寫入n個(gè)字節(jié)時(shí),擴(kuò)容才有可能發(fā)生。

舉個(gè)例子:

builder內(nèi)部slice容量為10。

builder內(nèi)部slice長度為5。

當(dāng)我們調(diào)用Grow(3)=擴(kuò)容操作并不會發(fā)生。因?yàn)楫?dāng)前的空余空間為5,足以提供3個(gè)字節(jié)的寫入。

當(dāng)我們調(diào)用Grow(7)=擴(kuò)容操作發(fā)生。因?yàn)楫?dāng)前的空余空間為5,已不足以提供7個(gè)字節(jié)的寫入。

關(guān)于上面的情形,如果這時(shí)我們調(diào)用Grow(7),則擴(kuò)容之后的實(shí)際容量是多少?

17還是12

實(shí)際上,是27。strings.Builder的Grow()方法是通過current_capacity*2+n(n就是你想要擴(kuò)充的容量)的方式來對內(nèi)部的slice進(jìn)行擴(kuò)容的。所以說最后的容量是10*2+7=27。當(dāng)你預(yù)定義strings.Builder容量的時(shí)候還要注意一點(diǎn)。調(diào)用WriteRune()和WriteString()時(shí),rune和string的字符可能不止1個(gè)字節(jié)。因?yàn)?,你懂的,UTF-8的原因。

funcmain(){

varb1strings.Builder

fmt.Println(b1.Len())//0

fmt.Println(b1.Cap())//0

b1.Grow(3)

fmt.Println(b1.Len())//0

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論