網站首頁 編程語言 正文
1.strings.Builder結構體
1.1strings.Builder結構體
// 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() 來獲取最終的字符串結果。為了節省內存分配,它通過使用指針技術將內部的 buffer bytes 轉換為字符串。所以 String() 方法在轉換的時候節省了時間和空間。
// 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 類方法將數據寫入 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)
有了它們,用戶可以根據輸入數據的不同類型(byte 數組,byte, rune 或者 string),選擇對應的寫入方法。
3.存儲原理
根據用法說明,我們通過調用 string.Builder 的寫入方法來寫入內容,然后通過調用 String() 方法來獲取拼接的字符串。那么 string.Builder 是如何組織這些內容的呢?
通過 slice,string.Builder 通過使用一個內部的 slice 來存儲數據片段。當開發者調用寫入方法的時候,數據實際上是被追加(append)到了其內部的 slice 上。
4.拷貝問題
strings.Builder 不推薦被拷貝。當你試圖拷貝 strings.Builder 并寫入的時候,你的程序就會崩潰。
你已經知道,strings.Builder 內部通過 slice 來保存和管理內容。slice 內部則是通過一個指針指向實際保存內容的數組。 當我們拷貝了 builder 以后,同樣也拷貝了其 slice 的指針。但是它仍然指向同一個舊的數組。當你對源 builder 或者拷貝后的 builder 寫入的時候,問題就產生了。另一個 builder 指向的數組內容也被改變了。這就是為什么 strings.Builder 不允許拷貝的原因。
func main() { var b1 strings.Builder b1.WriteString("ABC") b2 := b1 b2.WriteString("DEF")//出錯在這一行,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 }
但對于一個未寫入任何東西的空內容 builder 則是個例外。我們可以拷貝空內容的 builder 而不報錯。
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 會在以下方法中檢測拷貝操作:
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 是通過其內部的 slice 來儲存內容的。當你調用寫入方法的時候,新的字節數據就被追加到 slice 上。如果達到了 slice 的容量(capacity)限制,一個新的 slice 就會被分配,然后老的 slice 上的內容會被拷貝到新的 slice 上。當 slice 長度很大時,這個操作就會很消耗資源甚至引起 內存問題。我們需要避免這一情況。
關于 slice,Go 語言提供了 make([]TypeOfSlice, length, capacity) 方法在初始化的時候預定義它的容量。這就避免了因達到最大容量而引起擴容。
strings.Builder 同樣也提供了 Grow() 來支持預定義容量。當我們可以預定義我們需要使用的容量時,strings.Builder 就能避免擴容而創建新的 slice 了。
當調用 Grow() 時,我們必須定義要擴容的字節數(n)。 Grow() 方法保證了其內部的 slice 一定能夠寫入 n 個字節。只有當 slice 空余空間不足以寫入 n 個字節時,擴容才有可能發生。
舉個例子:
builder 內部 slice 容量為 10。
builder 內部 slice 長度為 5。
當我們調用 Grow(3) => 擴容操作并不會發生。因為當前的空余空間為 5,足以提供 3 個字節的寫入。
當我們調用 Grow(7) => 擴容操作發生。因為當前的空余空間為 5,已不足以提供 7 個字節的寫入。
關于上面的情形,如果這時我們調用 Grow(7),則擴容之后的實際容量是多少?
17 還是 12?
實際上,是 27。strings.Builder 的 Grow() 方法是通過 current_capacity * 2 + n (n 就是你想要擴充的容量)的方式來對內部的 slice 進行擴容的。所以說最后的容量是 10*2+7 = 27。 當你預定義 strings.Builder 容量的時候還要注意一點。調用 WriteRune() 和 WriteString() 時,rune 和 string 的字符可能不止 1 個字節。因為,你懂的,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 也不支持并行的讀或者寫。所以我們們要稍加注意。
可以試一下,通過同時給 strings.Builder 添加 1000 個字符:
通過運行,你會得到不同長度的結果。但它們都不到 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())) /* 第一次運行輸出:946 第二次運行輸出:933 第三次運行輸出:900 */ }
原文鏈接:https://blog.csdn.net/qq_53267860/article/details/126778278
相關推薦
- 2022-04-15 Android開發Jetpack組件Room用例講解_Android
- 2022-04-04 react安裝報錯ReactDOM.render is no longer supported in
- 2022-07-10 Callable接口的使用詳解
- 2022-11-07 C語言內存函數?memcpy,memmove?,memcmp_C 語言
- 2022-03-09 軟件構建工具makefile基礎講解_C 語言
- 2022-07-17 C#實現子類與父類的相互轉換_C#教程
- 2022-06-08 ASP.NET?Core中的靜態文件_基礎應用
- 2023-11-14 Kubernetes常用命令(持續更新)
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支