欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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ì)在以下方法中檢測拷貝操作:

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 長度很大時(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 長度為 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ì)得到不同長度的結(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并發(fā)繞不開的重要組件之Goroutine詳解

    Golang并發(fā)繞不開的重要組件之Goroutine詳解

    Goroutine、Channel、Context、Sync都是Golang并發(fā)編程中的幾個(gè)重要組件,這篇文中主要為大家介紹了Goroutine的相關(guān)知識(shí),需要的可以參考一下
    2023-06-06
  • golang 占位符和fmt常見輸出介紹

    golang 占位符和fmt常見輸出介紹

    這篇文章主要介紹了golang 占位符和fmt常見輸出介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go操作Kafka和Etcd方法詳解

    Go操作Kafka和Etcd方法詳解

    這篇文章主要為大家介紹了Go操作Kafka和Etcd方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 詳解go 中的 fmt 占位符

    詳解go 中的 fmt 占位符

    這篇文章主要介紹了go 中的 fmt 占位符,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • Go語言類型轉(zhuǎn)換的方式有哪些

    Go語言類型轉(zhuǎn)換的方式有哪些

    本文主要介紹了Go語言類型轉(zhuǎn)換的方式有哪些,類型轉(zhuǎn)換主要有4種,分別為斷言類型轉(zhuǎn)換、顯式類型轉(zhuǎn)換、隱式類型轉(zhuǎn)換、強(qiáng)制類型轉(zhuǎn)換,感興趣的可以了解一下
    2023-11-11
  • go語言的sql包原理與用法分析

    go語言的sql包原理與用法分析

    這篇文章主要介紹了go語言的sql包原理與用法,較為詳細(xì)的分析了Go語言里sql包的結(jié)構(gòu)、相關(guān)函數(shù)與使用方法,需要的朋友可以參考下
    2016-07-07
  • 簡單聊聊Golang中defer預(yù)計(jì)算參數(shù)

    簡單聊聊Golang中defer預(yù)計(jì)算參數(shù)

    在golang當(dāng)中defer代碼塊會(huì)在函數(shù)調(diào)用鏈表中增加一個(gè)函數(shù)調(diào)用,下面這篇文章主要給大家介紹了關(guān)于Golang中defer預(yù)計(jì)算參數(shù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • Go語言入門教程之基礎(chǔ)語法快速入門

    Go語言入門教程之基礎(chǔ)語法快速入門

    這篇文章主要介紹了Go語言入門教程之基礎(chǔ)語法快速入門,本文講解了值類型、變量、常量、循環(huán)、條件語句、條件枚舉等內(nèi)容,需要的朋友可以參考下
    2014-11-11
  • Golang中for循環(huán)的用法示例詳解

    Golang中for循環(huán)的用法示例詳解

    for循環(huán)就是讓一段代碼循環(huán)的執(zhí)行,接下來通過本文給大家講解Golang中for循環(huán)的用法,代碼簡單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • 詳解Go語言RESTful JSON API創(chuàng)建

    詳解Go語言RESTful JSON API創(chuàng)建

    這篇文章主要介紹了詳解Go語言RESTful JSON API創(chuàng)建,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05

最新評(píng)論