最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

GoLang strings.Builder底層實(shí)現(xiàn)方法詳解

 更新時(shí)間:2022年10月12日 09:41:39   作者:~龐貝  
自從學(xué)習(xí)go一個(gè)月以來,我多少使用了一下strings.Builder,略有心得。你也許知道它,特別是你了解bytes.Buffer的話。所以我在此分享一下我的心得,并希望能對(duì)你有所幫助

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

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

// A Builder is used to efficiently build a string using Write methods.
// It minimizes memory copying. The zero value is ready to use.
// Do not copy a non-zero Builder.
type Builder struct {
	addr *Builder // of receiver, to detect copies by value
	buf  []byte
}

1.2Write方法

// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *Builder) Write(p []byte) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, p...)
	return len(p), nil
}

1.3WriteByte方法

// WriteByte appends the byte c to b's buffer.
// The returned error is always nil.
func (b *Builder) WriteByte(c byte) error {
	b.copyCheck()
	b.buf = append(b.buf, c)
	return nil
}

1.4WriteRune方法

// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
// It returns the length of r and a nil error.
func (b *Builder) WriteRune(r rune) (int, error) {
	b.copyCheck()
	// Compare as uint32 to correctly handle negative runes.
	if uint32(r) < utf8.RuneSelf {
		b.buf = append(b.buf, byte(r))
		return 1, nil
	}
	l := len(b.buf)
	if cap(b.buf)-l < utf8.UTFMax {
		b.grow(utf8.UTFMax)
	}
	n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
	b.buf = b.buf[:l+n]
	return n, nil
}

1.5.WriteString方法

// 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
}

1.6String方法

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

// String returns the accumulated string.
func (b *Builder) String() string {
	return *(*string)(unsafe.Pointer(&b.buf))
}

1.7Len方法

// Len returns the number of accumulated bytes; b.Len() == len(b.String()).
func (b *Builder) Len() int { return len(b.buf) }

1.8Cap方法

// Cap returns the capacity of the builder's underlying byte slice. It is the
// total space allocated for the string being built and includes any bytes
// already written.
func (b *Builder) Cap() int { return cap(b.buf) }

1.9Reset方法

// Reset resets the Builder to be empty.
func (b *Builder) Reset() {
	b.addr = nil
	b.buf = nil
}

1.10Grow方法

// Grow grows b's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to b
// without another allocation. If n is negative, Grow panics.
func (b *Builder) Grow(n int) {
	b.copyCheck()
	if n < 0 {
		panic("strings.Builder.Grow: negative count")
	}
	if cap(b.buf)-len(b.buf) < n {
		b.grow(n)
	}
}

1.11grow方法

// grow copies the buffer to a new, larger buffer so that there are at least n
// bytes of capacity beyond len(b.buf).
func (b *Builder) grow(n int) {
	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
	copy(buf, b.buf)
	b.buf = buf
}

1.12copyCheck方法

func (b *Builder) copyCheck() {
	if b.addr == nil {
		// This hack works around a failing of Go's escape analysis
		// that was causing b to escape and be heap allocated.
		// See issue 23382.
		// TODO: once issue 7921 is fixed, this should be reverted to
		// just "b.addr = b".
		b.addr = (*Builder)(noescape(unsafe.Pointer(b)))
	} else if b.addr != b {
		panic("strings: illegal use of non-zero Builder copied by value")
	}
}

2.strings.Builder介紹

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

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

func (b *Builder) WriteByte(c byte) error

func (b *Builder) WriteRune(r rune) (int, error)

func (b *Builder) WriteString(s string) (int, error)

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

3.存儲(chǔ)原理

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

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

4.拷貝問題

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

你已經(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)你對(duì)源 builder 或者拷貝后的 builder 寫入的時(shí)候,問題就產(chǎn)生了。另一個(gè) builder 指向的數(shù)組內(nèi)容也被改變了。這就是為什么 strings.Builder 不允許拷貝的原因。

func main() {
	var b1 strings.Builder
	b1.WriteString("ABC")
	b2 := b1
	b2.WriteString("DEF")//出錯(cuò)在這一行,panic: strings: illegal use of non-zero Builder copied by value
}
func main() {
	var b1 strings.Builder
	b1.WriteString("ABC")
	b2 := b1
	fmt.Println(b2.String())//ABC
}
func main() {
	var b1 strings.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
}

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

func main() {
	var b1 strings.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 會(huì)在以下方法中檢測(cè)拷貝操作:

Grow(n int)

Write(p []byte)

WriteRune(r rune)

WriteString(s string)

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

func main() {
	// Reset()
	// Len()
	// String()
	var b1 strings.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 來儲(chǔ)存內(nèi)容的。當(dāng)你調(diào)用寫入方法的時(shí)候,新的字節(jié)數(shù)據(jù)就被追加到 slice 上。如果達(dá)到了 slice 的容量(capacity)限制,一個(gè)新的 slice 就會(huì)被分配,然后老的 slice 上的內(nèi)容會(huì)被拷貝到新的 slice 上。當(dāng) slice 長(zhǎng)度很大時(shí),這個(gè)操作就會(huì)很消耗資源甚至引起 內(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ū)懭?n 個(gè)字節(jié)。只有當(dāng) slice 空余空間不足以寫入 n 個(gè)字節(jié)時(shí),擴(kuò)容才有可能發(fā)生。

舉個(gè)例子:

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

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

當(dāng)我們調(diào)用 Grow(3) => 擴(kuò)容操作并不會(huì)發(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ò)充的容量)的方式來對(duì)內(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 的原因。

func main() {
	var b1 strings.Builder
	fmt.Println(b1.Len()) //0
	fmt.Println(b1.Cap()) //0
	b1.Grow(3)
	fmt.Println(b1.Len()) //0
	fmt.Println(b1.Cap()) //3
	b1.Grow(1)
	fmt.Println(b1.Len()) //0
	fmt.Println(b1.Cap()) //3
}
func main() {
	a := strings.Builder{}
	a.Grow(11)
	fmt.Println(a.Len()) //0
	fmt.Println(a.Cap()) //11
	a.WriteRune('李')
	a.WriteRune('陸')
	a.WriteRune('豪')
	a.WriteRune('Z')
	a.WriteRune('Z')
	fmt.Println(a.Len()) //11
	fmt.Println(a.Cap()) //11	
}

7.不支持并行讀寫

和 bytes.Buffer 一樣,strings.Builder 也不支持并行的讀或者寫。所以我們們要稍加注意。

可以試一下,通過同時(shí)給 strings.Builder 添加 1000 個(gè)字符:

通過運(yùn)行,你會(huì)得到不同長(zhǎng)度的結(jié)果。但它們都不到 1000。

func main() {
	var b strings.Builder
	n := 0
	var wait sync.WaitGroup
	for n < 1000 {
		wait.Add(1)
		go func() {
			b.WriteString("1")
			n++
			wait.Done()
		}()
	}
	wait.Wait()
	fmt.Println(len(b.String()))
	/*
			第一次運(yùn)行輸出:946
		   第二次運(yùn)行輸出:933
		 第三次運(yùn)行輸出:900
	*/
}

到此這篇關(guān)于GoLang strings.Builder底層實(shí)現(xiàn)方法詳解的文章就介紹到這了,更多相關(guān)GoLang strings.Builder內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang關(guān)鍵字select的常用用法總結(jié)

    Golang關(guān)鍵字select的常用用法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了golang中select關(guān)鍵字的常用用法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-10-10
  • 基于go語言實(shí)現(xiàn)圖片驗(yàn)證碼的代碼示例

    基于go語言實(shí)現(xiàn)圖片驗(yàn)證碼的代碼示例

    這篇文章主要為大家詳細(xì)介紹了基于go語言實(shí)現(xiàn)圖片驗(yàn)證碼的代碼示例,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • Golang基于epoll實(shí)現(xiàn)最簡(jiǎn)單網(wǎng)絡(luò)通信框架

    Golang基于epoll實(shí)現(xiàn)最簡(jiǎn)單網(wǎng)絡(luò)通信框架

    這篇文章主要為大家詳細(xì)介紹了Golang如何基于epoll實(shí)現(xiàn)最簡(jiǎn)單網(wǎng)絡(luò)通信框架,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)
    2023-06-06
  • Golang驗(yàn)證器之validator是使用詳解

    Golang驗(yàn)證器之validator是使用詳解

    Validator是一個(gè)?Golang?的第三方庫,用于對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),常用于?API?的開發(fā)中,對(duì)客戶端發(fā)出的請(qǐng)求數(shù)據(jù)進(jìn)行嚴(yán)格校驗(yàn),防止惡意請(qǐng)求。本文通過示例詳細(xì)講解了Validator的使用,需要的可以參考一下
    2022-08-08
  • Golang語言的多種變量聲明方式與使用場(chǎng)景詳解

    Golang語言的多種變量聲明方式與使用場(chǎng)景詳解

    Golang當(dāng)中的變量類型和C/C++比較接近,一般用的比較多的也就是int,float和字符串,下面這篇文章主要給大家介紹了關(guān)于Golang語言的多種變量聲明方式與使用場(chǎng)景的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • Go中的 panic / recover 簡(jiǎn)介與實(shí)踐記錄

    Go中的 panic / recover 簡(jiǎn)介與實(shí)踐記錄

    這篇文章主要介紹了Go中的 panic / recover 簡(jiǎn)介與實(shí)踐,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • golang不到30行代碼實(shí)現(xiàn)依賴注入的方法

    golang不到30行代碼實(shí)現(xiàn)依賴注入的方法

    這篇文章主要介紹了golang不到30行代碼實(shí)現(xiàn)依賴注入的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • Go使用select切換協(xié)程入門詳解

    Go使用select切換協(xié)程入門詳解

    這篇文章主要為大家介紹了Go使用select切換協(xié)程入門詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go 如何使用原始套接字捕獲網(wǎng)卡流量

    Go 如何使用原始套接字捕獲網(wǎng)卡流量

    為了減少對(duì)環(huán)境的依賴可以使用原始套接字捕獲網(wǎng)卡流量,然后使用?gopacket?的協(xié)議解析功能,這樣就省去了解析這部分的工作量,正確性也可以得到保證,同時(shí) CGO 也可以關(guān)閉,這篇文章主要介紹了Go 使用原始套接字捕獲網(wǎng)卡流量,需要的朋友可以參考下
    2024-07-07
  • golang?Strings包使用總結(jié)

    golang?Strings包使用總結(jié)

    Go語言在處理字符串時(shí),strings包提供了豐富的函數(shù),如常用的strings.Contains檢查是否包含子串,strings.Join拼接字符串?dāng)?shù)組,strings.Split切割字符串等,熟悉這些函數(shù)能有效提高編程效率,尤其是在算法競(jìng)賽或筆試題中
    2021-03-03

最新評(píng)論

昭苏县| 富蕴县| 安塞县| 钟山县| 宁远县| 宝清县| 同江市| 陇西县| 偃师市| 高平市| 吐鲁番市| 项城市| 乌审旗| 历史| 辽阳市| 白玉县| 金川县| 泸溪县| 工布江达县| 宁南县| 黎川县| 陆河县| 阳春市| 吕梁市| 资中县| 鄂伦春自治旗| 乃东县| 内黄县| 平乐县| 巫山县| 新乡县| 邢台县| 武隆县| 闵行区| 广西| 深水埗区| 漠河县| 丘北县| 滨海县| 上栗县| 昌乐县|