Go語言的數(shù)據(jù)結(jié)構(gòu)

整理下Go語言提供的數(shù)據(jù)結(jié)構(gòu)

一留晚、順序數(shù)據(jù)結(jié)構(gòu)

1.1. 數(shù)組

語言自帶。值得一提的是調(diào)函數(shù)的時(shí)候如果傳數(shù)組是值傳遞(復(fù)制整個(gè)數(shù)組),這點(diǎn)和java差別很大

func set(arr [3]int){
    arr[0]=453532
}

func setPointer(arr *[3]int){
    arr[0]=453532
}

func Test(t *testing.T) {
    arr:=[...]int{1,2,3}
    set(arr)
    fmt.Println(arr)  // [1 2 3]
    setPointer(&arr)
    fmt.Println(arr)  //[453532 2 3]

}

1.2. 動(dòng)態(tài)數(shù)組

可以把slice理解成java的ArrayList
但遺憾的是他沒有ArrayList好用。

把slice當(dāng)ArrayList用存在的坑:

  • slice是個(gè)view侨嘀,調(diào)別的函數(shù)append修改后,原先slice的內(nèi)容沒變@赏簟(見下圖)


    image.png

https://blog.csdn.net/weixin_40762352/article/details/79576616

解法:
a.自己封裝一個(gè)ArrayList
b.調(diào)別的函數(shù)時(shí)哄孤,讓函數(shù)返回slice凝危,類似于a=append(a,1)
c.傳slice的指針(自己改成引用傳遞)

構(gòu)造slice

http://www.cheat-sheets.org/saved-copy/go-lang-cheat-sheet-master.20181212/golang_refcard.pdf

image.png

image.png

make([]T,len) 不傳cap時(shí)寺晌,cap等于len

  • 零值是nil


    image.png

Q: ans:=make([]bool,n) 默認(rèn)值?
A:

func Test(t *testing.T) {
    ages:=make([]bool,2)
    AssertTrue(!ages[0],t)
    AssertTrue(!ages[1],t)
}

實(shí)現(xiàn)原理:cap是啥? slice的肚子里有什么?

image.png
  • cap是干嘛的?
    擴(kuò)展的時(shí)候,比如s2:=s1[3:5] 不能超過cap


    image.png

    append時(shí)候如果cap不夠,能動(dòng)態(tài)擴(kuò)容(自動(dòng)開個(gè)新array,拷貝過去)

cap vs len of slice in golang

image.png

實(shí)現(xiàn)原理: slice 怎么擴(kuò)容

image.png

https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-array-and-slice/#325-%E6%8B%B7%E8%B4%9D%E5%88%87%E7%89%87

https://segmentfault.com/a/1190000017341615

https://juejin.cn/post/6844903812331732999#heading-2

  • 有沒有縮容設(shè)計(jì)
    java ArrayList提供了縮容方法,但沒有自動(dòng)縮容機(jī)制
    go沒縮容機(jī)制,也沒提供這樣的api金砍。如有必要琅绅,可以用copy

Go 本身沒有切片縮容后,底層數(shù)組不會(huì)被釋放掉县貌。刪除次數(shù)多了,會(huì)占用很多內(nèi)存。
可以用copy的方法,創(chuàng)建新的切片和底層數(shù)組楞泼。并把原來的切片置nil。
https://cloud.tencent.com/developer/article/1720270

判斷內(nèi)容相等

可以用reflect.DeepEqual
https://golang.org/pkg/reflect/#DeepEqual

打印slice

fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)

https://www.runoob.com/go/go-slice.html

append

http://shouce.jb51.net/gopl-zh/ch4/ch4-02.html

concatenate two slices

https://stackoverflow.com/questions/16248241/concatenate-two-slices-in-go

append([]int{1,2}, []int{3,4}...)

  • 那要是 append nil... 呢?
    正常,不會(huì)panic, 相當(dāng)于沒append:
func main() {
    a := make([]int, 1)
    var b []int
    a = append(a, b...)
    a = append(a, nil...)
    fmt.Println(a)  // [0]
}

copy slice

使用copy函數(shù)
https://www.geeksforgeeks.org/how-to-copy-one-slice-into-another-slice-in-golang/
但是注意創(chuàng)建的新slice長度提前要設(shè)置好,The builtin copy(dst, src) copies min(len(dst), len(src)) elements.
https://stackoverflow.com/questions/30182538/why-cant-i-duplicate-a-slice-with-copy

arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

Output (as expected):

[1 2 3]
[1 2 3]

1.3. Queue

1.3.1. 無界隊(duì)列 (unbounded)

非并發(fā)安全

slice就能當(dāng)queue用。
另外bytes.buffer可以當(dāng)byte類型的queue,其實(shí)內(nèi)部就是[]byte
https://www.kancloud.cn/digest/batu-go/153538

并發(fā)安全的無界隊(duì)列

chan 是有界隊(duì)列屎债,go 團(tuán)隊(duì)不愿意在標(biāo)準(zhǔn)庫里加入無界隊(duì)列
可以自己封裝盆驹,思路是在兩個(gè)chan之間加個(gè)中轉(zhuǎn) slice硝枉,單協(xié)程操作該slice 避免并發(fā)問題正压。見 https://blog.zeromake.com/pages/ultimete-channel/

1.3.2. 有界 (bounded)

非并發(fā)安全

沒有給單線程用的,需要自己拿slice封裝

并發(fā)安全的有界阻塞隊(duì)列:channel

channel是有界阻塞隊(duì)列嘉裤,倒是可以寫入的時(shí)候判斷栖博,以實(shí)現(xiàn)有界非阻塞
https://www.coder.work/article/24623

默認(rèn)是 unbuffered chan, 隊(duì)列長度0屑宠。可以在 make 時(shí)設(shè)置隊(duì)列長度


image.png
原理

Q: channel肚子里是啥仇让,內(nèi)部實(shí)現(xiàn)原理侨把?
A:channel是個(gè)指針(chan是引用類型)妹孙,指向的struct runtime.hchan,是個(gè)有鎖的環(huán)形隊(duì)列
https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-channel/

image.png

13 | Channel:另辟蹊徑获枝,解決并發(fā)問題

理論上可以實(shí)現(xiàn) lock-free queue, 但是 golang 官方的 issue 一直沒進(jìn)展蠢正。作為替代,社區(qū)有實(shí)現(xiàn) lock-free queue 的庫
https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-channel/
https://github.com/golang/go/issues/8899#issuecomment-269321360

特殊用法

Q: channel的所有方法都是阻塞方法省店,想非阻塞咋辦嚣崭?
A: 用select+default自己封裝非阻塞方法:
見effective go p102


image.png

Q: 會(huì) panic 的情況?
總共有 3 種:

  • close 為 nil 的 chan;
  • send 已經(jīng) close 的 chan懦傍;
  • close 已經(jīng) close 的 chan雹舀。

Q: 如何用 chan 做全體廣播?
a. 自己寫個(gè)消費(fèi)者去消費(fèi)chan,然后廣播通知所有訂閱者
b. 利用close
https://www.cnblogs.com/faithfu/p/12068414.html

1.4. Deque

非并發(fā)安全

go 的slice 不好實(shí)現(xiàn)"從左側(cè) enque", 要找其他數(shù)據(jù)結(jié)構(gòu)粗俱。
java有兩種實(shí)現(xiàn)说榆,鏈表和環(huán)形隊(duì)列
Go的list包下有個(gè)鏈表,API很不deque,但是能湊合用
ring包下的環(huán)沒法動(dòng)態(tài)擴(kuò)容签财,API也比較沙雕串慰,沒法拿來當(dāng)deque
https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter03/03.3.md

鑒于自帶鏈表的API不好用,自己寫了一個(gè):
https://github.com/seeflood/Copy-Paste-Data-Structures/tree/master/src/main/go/io/github/seeflood/copy-paste-ds/deque

并發(fā)安全

chan 就是唱蒸,內(nèi)部是有鎖環(huán)形隊(duì)列

1.5. PriorityQueue

https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter03/03.3.html
標(biāo)準(zhǔn)庫有邦鲫,API設(shè)計(jì)的很不好用,想用得實(shí)現(xiàn)一堆接口神汹。想用IntHeap都得自己寫(標(biāo)準(zhǔn)庫里沒有IntHeap)庆捺;
heap包下面的unit test代碼里有個(gè)IntHeap,因此一個(gè)偷懶的做法是:想用IntHeap的時(shí)候可以去heap包下面的unit test代碼里把IntHeap復(fù)制粘貼到自己的項(xiàng)目

鑒于API過于難用屁魏,自己封裝了個(gè)SliceHeap

1.6. 臨時(shí)對(duì)象池 sync.Pool

sync.Pool 數(shù)據(jù)類型用來保存一組可獨(dú)立訪問的臨時(shí)對(duì)象滔以。請(qǐng)注意這里加粗的“臨時(shí)”這兩個(gè)字,它說明了 sync.Pool 這個(gè)數(shù)據(jù)類型的特點(diǎn)蚁堤,也就是說醉者,它池化的對(duì)象會(huì)在未來的某個(gè)時(shí)候被毫無預(yù)兆地移除掉。而且披诗,如果沒有別的對(duì)象引用這個(gè)被移除的對(duì)象的話撬即,這個(gè)被移除的對(duì)象就會(huì)被垃圾回收掉。

實(shí)現(xiàn)的有問題呈队,有需要還是找三方庫:


image.png

詳見 https://time.geekbang.org/column/article/301716

  • 連接池
    一般不用 sync.Pool

事實(shí)上剥槐,我們很少會(huì)使用 sync.Pool 去池化連接對(duì)象,原因就在于宪摧,sync.Pool 會(huì)無通知地在某個(gè)時(shí)候就把連接移除垃圾回收掉了粒竖,而我們的場景是需要長久保持這個(gè)連接,所以几于,我們一般會(huì)使用其它方法來池化連接

可以自己用隊(duì)列實(shí)現(xiàn)(用 channel 或者 slice)蕊苗,或者用三方庫

  • 協(xié)程池 worker pool
    標(biāo)準(zhǔn)庫沒有,要用三方庫

二沿彭、Map

2.1. hashmap

自帶的map就是
http://shouce.jb51.net/gopl-zh/ch4/ch4-03.html

2.1.1. 創(chuàng)建

Q: 用var a map[xxx]xxx這樣聲明時(shí)朽砰,會(huì)創(chuàng)建新的map嗎?
實(shí)測不會(huì)喉刘,只生成nil瞧柔;別的寫法都會(huì)創(chuàng)建新map


func Test(t *testing.T) {
    var ages map[string]int

    if ages == nil {
        fmt.Println("var map[string]int==nil")
    }

    ages = make(map[string]int)
    if ages == nil {
        fmt.Println("make(map[string]int)==nil")
    }


    ages=map[string]int{}
    if ages == nil {
        fmt.Println("map[string]int{}==nil")
    }

}

打印結(jié)果:
var map[string]int==nil

https://books.studygolang.com/gopl-zh/ch4/ch4-03.html

  • nil的map(沒初始化的map)能讀能刪不能寫


    image.png

    slice用慣了可能會(huì)懶得寫初始化,直接

    var s []int
    s = append(s, 1)

這樣反正append能寫nil slice睦裳。但是map必須先初始化才能寫造锅!

2.1.2. get與containsKey

age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }

不加第二個(gè)變量ok也不會(huì)panic:


image.png

2.1.3. 遍歷

for range

m := map[string]int{
    "hello": 100,
    "world": 200,
}
for key, value := range m {
    fmt.Println(key, value)
}
Q: 如果 map 是 nil, 會(huì) panic 么

不會(huì)

package main

import "fmt"

func main() {
    var m map[string]string
    for k, _ := range m {
        fmt.Printf("k :%v\n",k)
    }
    fmt.Println("end")
}
Q: 能否在for range的過程中添加/刪除元素?

A:
https://stackoverflow.com/questions/23229975/is-it-safe-to-remove-selected-keys-from-map-within-a-range-loop
刪除可以,刪掉的不會(huì)被遍歷到廉邑;添加雖然可以哥蔚,但是加進(jìn)去的可能會(huì)在當(dāng)次循環(huán)被遍歷到倒谷、也可能不會(huì),有不確定性肺素。

2.1.4. 判斷兩個(gè)map相等

How to test the equivalence of maps in Golang?
https://www.geeksforgeeks.org/comparing-maps-in-golang/

reflect.DeepEqual(a, b)

2.1.5. golang中如何判斷兩個(gè)struct相等

https://www.geeksforgeeks.org/structure-equality-in-golang/

==和DeeplyEqual()都行恨锚,肚子里的值一樣就算相等,不是java那種比較指向的內(nèi)存對(duì)象相等

golang里的map用==比較兩個(gè)key相等倍靡,因此實(shí)際上是比較struct肚子里的值猴伶。如果確實(shí)想比較兩個(gè)key是“同一個(gè)”,可以用指針當(dāng)key
https://stackoverflow.com/questions/29748003/map-with-function-pointer-as-key-in-go

2.1.6. 能否自定義map中元素判等的邏輯塌西?

不能他挎,map用==判斷key相等的
https://juejin.cn/post/6844903923166232589#heading-8

2.1.7. 想用<key1,key2,value>式的雙key map?

https://stackoverflow.com/questions/30529970/golang-map-with-multiple-keys-per-value
key放struct里就行

image.png

缺點(diǎn)是:如果修改 struct 肚子里的值捡需,就沒法再從 map 里取到原來對(duì)應(yīng)的 value 了办桨。


type mapKey struct {
    key int
}

func main() {
    var m = make(map[mapKey]string)
    var key = mapKey{10}


    m[key] = "hello"
    fmt.Printf("m[key]=%s\n", m[key])


    // 修改key的字段的值后再次查詢map,無法獲取剛才add進(jìn)去的值
    key.key = 100
    fmt.Printf("再次查詢m[key]=%s\n", m[key])
}

2.1.8. 內(nèi)部實(shí)現(xiàn)

golang中的map是一個(gè) 指針站辉,指向的struct是個(gè)哈希表
https://blog.csdn.net/K346K346/article/details/109559718
https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-hashmap/
https://zjykzk.github.io/post/cs/golang/map/
https://guidao.github.io/go_map.html

Q: 如何處理哈希碰撞
用拉鏈法來解決哈希碰撞的問題呢撞。哈希表的每個(gè)桶都只能存儲(chǔ) 8 個(gè)鍵值對(duì),一旦當(dāng)前哈希的某個(gè)桶超出 8 個(gè)饰剥,新的鍵值對(duì)就會(huì)存儲(chǔ)到哈希的溢出桶中殊霞。
(個(gè)人理解,本質(zhì)上是分塊鏈表?)


image.png

Q: 如何擴(kuò)容
隨著鍵值對(duì)數(shù)量的增加汰蓉,溢出桶的數(shù)量和哈希的裝載因子也會(huì)逐漸升高绷蹲,超過一定范圍就會(huì)觸發(fā)擴(kuò)容,擴(kuò)容會(huì)將桶的數(shù)量翻倍
具體來說顾孽,在以下兩種情況發(fā)生時(shí)觸發(fā)哈希的擴(kuò)容:

  • 裝載因子已經(jīng)超過 6.5祝钢;
  • 哈希使用了太多溢出桶;

2.2. ordered map (例如 TreeMap/SkipListMap 這樣的平衡搜索樹類的數(shù)據(jù)結(jié)構(gòu))

搜了下好像標(biāo)準(zhǔn)庫沒有
社區(qū)有

2.3. LRUCache

沒有
自己寫了一個(gè):
https://github.com/seeflood/Copy-Paste-Data-Structures/tree/master/src/main/go/io/github/seeflood/copy-paste-ds/map

2.4. ConcurrentHashMap

a. 通過改變線程模型若厚,讓單線程操作map

b. 用讀寫鎖自己封裝一個(gè)

c. 用第三方的實(shí)現(xiàn)拦英,分片加鎖

比如 https://github.com/orcaman/concurrent-map

d. 應(yīng)對(duì)特殊場景的 sync.Map

不常用,特殊場景會(huì)比 map+RWMutex 的方式性能好测秸;缺點(diǎn)是會(huì)丟失類型信息疤估,方法傳參、返回結(jié)果都是 interface{}
http://www.reibang.com/p/a2831dfa0a91
https://studygolang.com/articles/18070
https://colobu.com/2017/07/11/dive-into-sync-Map/

image.png
  • 內(nèi)部原理


    image.png

倆 map乞封,有一個(gè)僅用于讀的 read map,相當(dāng)于讀緩存
read map 有性能優(yōu)勢是因?yàn)?value 是個(gè) holder岗憋,holder 里能基于 cas 修改value
新增 kv 會(huì)寫進(jìn) dirty map (會(huì)加互斥鎖肃晚,不是讀寫鎖),達(dá)到一定條件( 緩存 miss 率太高仔戈、達(dá)到一定的值)后关串,會(huì)將dirty數(shù)據(jù)提升為read

https://tonybai.com/2020/11/10/understand-sync-map-inside-through-examples/

https://time.geekbang.org/column/article/301174

2.5. 其他并發(fā)安全的數(shù)據(jù)結(jié)構(gòu)

除了sync.Map和sync.Pool拧廊,GO就沒啥并發(fā)安全數(shù)據(jù)結(jié)構(gòu)了。見討論:
https://groups.google.com/g/golang-china/c/JdbR_CGo3ao/m/Uct3hj2wKCsJ

三晋修、Set

3.1. hashset

沒有吧碾,只能拿map模擬
https://stackoverflow.com/questions/34018908/golang-why-dont-we-have-a-set-datastructure

實(shí)現(xiàn)時(shí)注意,map[interface{}]struct{}比map[interface{}]bool好在空結(jié)構(gòu)體不占用空間:
https://geektutu.com/post/hpg-empty-struct.html

自己輪了一個(gè)
https://github.com/seeflood/Copy-Paste-Data-Structures/tree/master/src/main/go/io/github/seeflood/copy-paste-ds/set

3.2. bitset/bitmap

標(biāo)準(zhǔn)庫沒有墓卦。開源的有個(gè)github.com/willf/bitset
https://blog.cyeam.com/golang/2017/01/18/go-optimize-bitset

我也實(shí)現(xiàn)了個(gè)
https://github.com/seeflood/Copy-Paste-Data-Structures/blob/master/src/main/go/io/github/seeflood/copy-paste-ds/set/BitSet.go
實(shí)現(xiàn)方法參考https://books.studygolang.com/gopl-zh/ch6/ch6-05.html

另外倦春,分塊bitmap(Roaring bitmap)有個(gè)開源的go實(shí)現(xiàn)
https://github.com/RoaringBitmap/roaring

四、字符串相關(guān)

4.1. string

http://shouce.jb51.net/gopl-zh/ch3/ch3-05.html
https://blog.golang.org/strings

4.1.1. 默認(rèn)是按字節(jié)訪問

In Go, a string is in effect a read-only slice of bytes
也就是說string是只讀的[]byte(也就是說string不可變)落剪,雖然你的字符串可能是UTF-8編碼睁本,但是語言提供的很多API(比如len(string)、隨機(jī)訪問字符s[1]等)不管你啥編碼忠怖,統(tǒng)一按字節(jié)訪問字符串……
那想按字符訪問字符串怎么辦呢堰?

求長度

關(guān)于求字符串長度,替代len(string)可以用utf8.RuneCountInString(s)凡泣,但是這API每次調(diào)用得遍歷解析一遍計(jì)算長度枉疼,時(shí)間(N)。java的String.length()是返回char[].length,時(shí)間O(1)

按字符訪問

關(guān)于按字符訪問鞋拟,for range是按字符訪問字符串骂维,但他是順序訪問,不支持隨機(jī)訪問


image.png

想隨機(jī)訪問严卖?得把字符串轉(zhuǎn)[]rune:

r := []rune(s)

但是你會(huì)發(fā)現(xiàn)席舍,轉(zhuǎn)成[]rune后,strings包下面的API都用不了了哮笆!
這設(shè)計(jì)来颤,真想罵娘!3碇狻福铅!

注1:strings包下的API只處理UTF-8編碼的string。
注2:Go語言源文件總是用UTF8編碼项阴,并且Go語言的文本字符串也以UTF8編碼的方式處理
注3:UTF-8編碼中滑黔,沒有任何字符的編碼是其它字符編碼的子串,或是其它編碼序列的子串环揽,因此搜索一個(gè)字符時(shí)只要搜索它的字節(jié)編碼序列即可略荡,不用擔(dān)心前后的上下文會(huì)對(duì)搜索結(jié)果產(chǎn)生干擾(說人話就是:做字符串匹配的時(shí)候,無腦用字節(jié)匹配就行歉胶,見下圖)


image.png

4.1.2. 判斷

判空

string的零值是""汛兜,也沒法賦值nil給string變量
https://tour.golang.org/basics/12

所以len(s)==0 或者s==""都可以拿來判空
https://stackoverflow.com/questions/18594330/what-is-the-best-way-to-test-for-an-empty-string-in-go

判斷相等

就用==就行。不區(qū)分大小寫用EqualFold
https://blog.csdn.net/oqqYuan1234567890/article/details/59110219

4.1.3. 類型轉(zhuǎn)換

[]rune []byte和string互相轉(zhuǎn)換

https://blog.csdn.net/dengming0922/article/details/80883574

https://stackoverflow.com/questions/29255746/how-encode-rune-into-byte-using-utf8

string和數(shù)字轉(zhuǎn)換

一種方法是用fmt.Sprintf返回一個(gè)格式化的字符串通今;
另一個(gè)方法是用strconv包的 strconv.Itoa. lintcode不讓用這個(gè)粥谬,leetcode 可以肛根。
http://shouce.jb51.net/gopl-zh/ch3/ch3-05.html

x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"

https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/04.7.md#4712-%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%8E%E5%85%B6%E5%AE%83%E7%B1%BB%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2

image.png
字符和數(shù)字互相轉(zhuǎn)換?

如果是 0-9 這樣的“單字符數(shù)字”(我自己起的名字)漏策,可以這樣轉(zhuǎn):

    b := 1 + '0' // int32
    fmt.Printf("Size: %d\nType: %s\nCharacter: %c\n", unsafe.Sizeof(b), reflect.TypeOf(b), b)

    rawNum := b - '0' // int32
    fmt.Println(rawNum)

如果是“多字符數(shù)字”派哲,就得當(dāng)做字符串和數(shù)字做轉(zhuǎn)換

Convert interface/any data structure to string

https://yourbasic.org/golang/interface-to-string/
Use fmt.Sprintf

var x interface{} = "abc"
str := fmt.Sprintf("%v", x)

另見《fmt.Sprintf vs string(data) vs String()》

4.1.4. 改

字符串的值是不可變的:一個(gè)字符串包含的字節(jié)序列永遠(yuǎn)不會(huì)被改變,當(dāng)然我們也可以給一個(gè)字符串變量分配一個(gè)新字符串值掺喻“沤欤可以像下面這樣將一個(gè)字符串追加到另一個(gè)字符串:

s := "left foot"
t := s
s += ", right foot"
這并不會(huì)導(dǎo)致原始的字符串值被改變,但是變量s將因?yàn)?=語句持有一個(gè)新的字符串值巢寡,但是t依然是包含原先的字符串值喉脖。

fmt.Println(s) // "left foot, right foot"
fmt.Println(t) // "left foot"
因?yàn)樽址遣豢尚薷牡模虼藝L試修改字符串內(nèi)部數(shù)據(jù)的操作也是被禁止的:

s[0] = 'L' // compile error: cannot assign to s[0]

拼接

java有StringBuilder這樣的字符串專用stack抑月,相應(yīng)的go里可以用的動(dòng)態(tài)數(shù)組有:

  • []byte,通過append拼接
  • bytes.Buffer,通過buffer.WriteString()往里面寫字符串
    其實(shí)bytes.Buffer內(nèi)部就是[]byte
  • strings.Builder

Examples &選型benchmark:
https://mojotv.cn/go/golang-most-efficient-string-join
https://geektutu.com/post/hpg-string-concat.html#1-2-benchmark-%E6%80%A7%E8%83%BD%E6%AF%94%E6%8B%BC
從benchmark看來树叽,有preallocate的性能好,沒有的話這三個(gè)差別不大

ref:
https://stackoverflow.com/questions/1760757/how-to-efficiently-concatenate-strings-in-go/47798475#47798475
http://www.reibang.com/p/df92c0ee6cc8

隨機(jī)訪問谦絮、修改
只改其中一兩個(gè)字符

https://stackoverflow.com/questions/37688457/how-to-replace-nth-char-from-a-string-in-go

方案1. reslice

chars = chars[:3] + "z" + chars[4:]

方案2. 從一開始就用[]byte,而不是string

func main() {
  var chars = []byte{'a', 'b', 'c', 'd', 'e', 'f'}
  fmt.Println(string(chars[3]))
  fmt.Printf("%T\n", chars)
  chars[3] = 'z'
  fmt.Println(string(chars))
}

方案3. 拷貝成別的數(shù)據(jù)結(jié)構(gòu)题诵,然后修改

func replaceAt(s string, i int, c rune) string {
    r := []rune(s)
    r[i] = c
    return string(r)
}
替換

strings包下面的替換不支持正則
https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/04.7.md#474-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%9B%BF%E6%8D%A2

想用正則替換得用regexp包
https://www.geeksforgeeks.org/golang-replacing-all-string-which-matches-with-regular-expression/

寫正則表達(dá)式的時(shí)候用...比較方便(字符串面值),使用反引號(hào)代替雙引號(hào)层皱。在原生的字符串面值中性锭,沒有轉(zhuǎn)義操作

其他操作

就用strings 和 strconv 包
https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/04.7.md

4.1.5.字符

java用char通吃,但go設(shè)計(jì)的比較爛叫胖,需要理解byte,uint8,int32以及rune.

https://golangbyexample.com/character-in-go/
http://c.biancheng.net/view/18.html
Go語言的字符有以下兩種:

  • 一種是 uint8 類型草冈,byte 類型是 uint8 的別名,代表了 ASCII 碼的一個(gè)字符瓮增。
  • 另一種是 rune 類型怎棱,代表一個(gè) UTF-8 字符,當(dāng)需要處理中文绷跑、日文或者其他復(fù)合字符時(shí)拳恋,則需要用到 rune 類型。rune 類型是int32的別名砸捏。
聲明字符變量

To declare either a byte or a rune we use single quotes. While declaring byte we have to specify the type, If we don’t specify the type, then the default type is meant as a rune.

To declare a string, we use double quotes or backquotes. Double quotes string honors escape character while back quotes string is a raw literal string and doesn’t honor any kind of escaping.

這些字符都是啥類型谬运?

s := "hello, world"
c:=s[0] // c is uint8
cnum := c - '0' //uint8
bytes := []byte(s)
x:=bytes[0] //x is uint8

test:='0' //int32.'0'是untyped constant
cnum32:=test-'0' //int32.'0'是untyped constant

直接訪問的話,得到的字符是uint8.byte是uint8的別名

r, size := utf8.DecodeRuneInString(s[1:]) // r is int32
for i, w := range "Hello, 世界" {
fmt.Printf("%d\t%q\t%d\n", i, w, w)
}
// w is int32

utf8解碼后是int32.for range自動(dòng)解碼垦藏。

As mentioned above, integers come in a couple of forms and each form has its own default type: int for simple constants like 123 or 0xFF or -14 and rune for quoted characters like 'a', '世' or '\r'.
https://blog.golang.org/constants#TOC_10.

國際化字符rune

rune是int32的別名梆暖,但是使用起來注意有區(qū)別,比如for range 字符串時(shí)掂骏,每個(gè)中文字符算3個(gè)長度轰驳,而for range []rune(字符串) 每個(gè)中文字符算一個(gè)長度:

for i, r := range "Hello, 世界" {
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
//...
//7 '世' 19990
//10 '界' 30028
for i, r := range []rune("Hello, 世界" ){
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
//...
//7 '世' 19990
//8 '界' 30028

4.2. Suffix Array

第一次看到有語言把后綴數(shù)組放進(jìn)了標(biāo)準(zhǔn)庫,關(guān)鍵放就放了,這個(gè)API設(shè)計(jì)的就像個(gè)玩具項(xiàng)目:
構(gòu)造方法居然只支持New([]byte) 不支持string不支持 []rune等滑废;
后綴數(shù)組理論上能做的事情很多,但是API只提供了個(gè)Lookup袜爪,同樣只支持[]byte
玩具項(xiàng)目都沒這么懶……
https://www.kancloud.cn/yetship/golang_standard_library_samples/527457

五蠕趁、樹相關(guān),圖相關(guān)

啥都沒有

其他數(shù)據(jù)結(jié)構(gòu)辛馆?用三方庫吧俺陋!

其他的基本都沒有,我寫了一些放到自己的Copy-Paste Data Structures 項(xiàng)目里了
https://github.com/seeflood/Copy-Paste-Data-Structures

比較有名的三方數(shù)據(jù)結(jié)構(gòu)庫:
https://github.com/Workiva/go-datastructures

Q:比較有名的concurrent data structure庫昙篙?
A:沒有腊状。
orcaman/concurrent-map這個(gè)連release都沒有,pr也很久沒合并過了苔可,看著比較練手缴挖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市焚辅,隨后出現(xiàn)的幾起案子映屋,更是在濱河造成了極大的恐慌,老刑警劉巖同蜻,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棚点,死亡現(xiàn)場離奇詭異,居然都是意外死亡湾蔓,警方通過查閱死者的電腦和手機(jī)瘫析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來默责,“玉大人贬循,你說我怎么就攤上這事∩邓浚” “怎么了甘有?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長葡缰。 經(jīng)常有香客問我亏掀,道長,這世上最難降的妖魔是什么泛释? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任滤愕,我火速辦了婚禮,結(jié)果婚禮上怜校,老公的妹妹穿的比我還像新娘间影。我一直安慰自己,他們只是感情好茄茁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布魂贬。 她就那樣靜靜地躺著巩割,像睡著了一般。 火紅的嫁衣襯著肌膚如雪付燥。 梳的紋絲不亂的頭發(fā)上宣谈,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音键科,去河邊找鬼闻丑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛勋颖,可吹牛的內(nèi)容都是我干的嗦嗡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼饭玲,長吁一口氣:“原來是場噩夢啊……” “哼侥祭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茄厘,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤卑硫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蚕断,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欢伏,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年亿乳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硝拧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葛假,死狀恐怖丽涩,靈堂內(nèi)的尸體忽然破棺而出旭咽,到底是詐尸還是另有隱情挑围,我是刑警寧澤雏蛮,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站带斑,受9級(jí)特大地震影響鼓寺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勋磕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一妈候、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挂滓,春花似錦苦银、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纺念。三九已至,卻和暖如春想括,著一層夾襖步出監(jiān)牢的瞬間柠辞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工主胧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人习勤。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓踪栋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親图毕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夷都,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容