Go語言中關(guān)于set的實(shí)現(xiàn)思考分析
Go 開發(fā)過程中有時(shí)我們需要集合(set)這種容器,但 Go 本身未內(nèi)置這種數(shù)據(jù)容器,故常常我們需要自己實(shí)現(xiàn),其實(shí)實(shí)現(xiàn)也很簡單。
附,推薦閱讀:github.com/Visforest/goset
map[xxx]struct{}
最常用和最容易想到的實(shí)現(xiàn)是使用 map
,如:
type StrSet struct{ data map[string]struct{} }
map
的 value 部分設(shè)計(jì)為 struct{}
類型是為了節(jié)省內(nèi)存空間。
map[interface{}]struct{}
上面實(shí)現(xiàn)的是 string 的 set,如果要其他類型的 set 就得再定義 Int8Set
、IntSet
、Float32Set
等等,很是繁瑣。
很多人可能會(huì)選擇這樣實(shí)現(xiàn) :
type Set struct { data map[interface{}]struct{} } // New creates a new Set func New(v ...interface{}) *Set { s := &Set{data: map[interface{}]struct{}{}} for _, ele := range v { s.data[ele] = struct{}{} } return s } // ... // ToList returns data slice func (s *Set) ToList() []interface{} { var data = make([]interface{}, len(s.data)) var i int for d := range s.data { data[i] = d i++ } return data }
這種方式有幾個(gè)問題:
執(zhí)行如下代碼:
func main() { var l1 = []int{1, 2, 3} var l2 = []int{4, 5, 6} var s = NewSet(l1, l2) for _, e := range s.ToList() { fmt.Println(e) } }
出錯(cuò):
panic: runtime error: hash of unhashable type []int
原因很簡單,[]int
是不能被 hash 計(jì)算的,即不能作為 map 的 key,讀者可以查閱 map key允許的類型。interface{}
這種“萬金油” 也可能是不合適的。
觀察下面代碼
func main() { var s = NewSet("a", "b", "c") var tmp []string for _, e := range s.ToList() { tmp = append(tmp, e.(string)) } test(tmp) }
test
函數(shù)不能直接拿 s.ToList()
作為入?yún)?,必須?s.ToList()
進(jìn)行轉(zhuǎn)換為 []string
,原因不言自明。
每次都要轉(zhuǎn)換明顯損失了編碼效率和執(zhí)行效率。
map[T comparable]struct{}
上面的弊端,可以用 泛型(generics)解決。
定義:
type Set[T comparable] struct { data map[T]struct{} } // New creates a new Set func NewSet[T comparable](v ...T) *Set[T] { s := &Set[T]{data: map[T]struct{}{}} for _, ele := range v { s.data[ele] = struct{}{} } return s } func (s *Set[T]) Add(v ...T) { for _, ele := range v { s.data[ele] = struct{}{} } } // ... // ToList returns data slice func (s *Set[T]) ToList() []T { var data = make([]T, len(s.data)) var i int for d := range s.data { data[i] = d i++ } return data }
使用:
func test1(data []string) { // ... } func test2(data []float64) { // ... } func main() { var s1 = NewSet("a", "b", "c") test1(s1.ToList()) var s2 = NewSet(1.3, 2.2, 3) test2(s2.ToList()) }
type IntSet = Set[int]
上面的 Set 是個(gè)通用 set,類型混用時(shí)自己可能會(huì)被誤導(dǎo)。我們可以定義專用數(shù)據(jù)類型的 set,且代碼不需要很多。
type IntSet = Set[int] func NewIntSet(v ...int) *IntSet { return NewSet[int](v...) }
使用:
func main() { var s = NewIntSet(1, 2, 3) test3(s.ToList()) // 編譯錯(cuò)誤 // s.Add("a", "b", "c") }
fifo set
通常 set 是無序的,上面的實(shí)現(xiàn)也都是無序的,但有的場景下我們需要有序的 set,比如fifo set,sorted set。這里以 fifo set 為例,討論下其實(shí)現(xiàn)。
為了兼顧查找效率和有序特性,可以使用 map
+ array
/ double linkedlist
,考慮到數(shù)據(jù)的添加、刪除以及內(nèi)存使用,double linkedlist
有比 array
顯著的優(yōu)勢(shì)。
type setNode[T comparable] struct { val T pre *setNode[T] next *setNode[T] } type FifoSet[T comparable] struct { head *setNode[T] tail *setNode[T] data map[T]*setNode[T] } // add data, make it first in first out func (l *FifoSet[T]) Add(v ...T) { if len(v) == 0 { return } var i int if l.head == nil { // first node n := &setNode[T]{ val: v[i], } l.head = n l.tail = n l.data[v[i]] = n i++ } for ; i < len(v); i++ { if _, ok := l.data[v[i]]; !ok { // when missing, insert n := &setNode[T]{ val: v[i], pre: l.tail, next: nil, } l.tail.next = n l.tail = n l.data[v[i]] = n } } }
使用:
func main() { var s = NewFifoSet[string]() s.Add("e", "a", "b", "a", "c", "b") // e // a // b // c for _, v := range s.ToList() { fmt.Println(v) } }
sorted set
其實(shí) sorted set 與 fifo set 實(shí)現(xiàn)很像,只是略有區(qū)別,這里就略過了。
有興趣的可以閱讀筆者的 github.com/Visforest/goset,或者自己嘗試自己實(shí)現(xiàn)下。
以上就是Go語言中關(guān)于set的實(shí)現(xiàn)思考分析的詳細(xì)內(nèi)容,更多關(guān)于Go set的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang 進(jìn)度條功能實(shí)現(xiàn)示例
這篇文章主要介紹了golang 進(jìn)度條功能實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08golang實(shí)現(xiàn)PHP數(shù)組特性的方法
我們做業(yè)務(wù)過程中,對(duì)應(yīng)強(qiáng)類型語言使用有個(gè)痛點(diǎn),就是使用變量之前一定要定義變量類型,那么本文就來介紹一下golang實(shí)現(xiàn)PHP數(shù)組特性的方法2021-12-12goland配置自動(dòng)注釋的實(shí)現(xiàn)
本文主要介紹了goland配置自動(dòng)注釋的實(shí)現(xiàn),文中通過圖文示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08Go Web下gin框架的模板渲染的實(shí)現(xiàn)
Gin框架是目前非常流行的Go語言Web框架之一,作為一個(gè)輕量級(jí)的框架,Gin提供了豐富的功能和靈活的架構(gòu),本文就來介紹下Go Web下gin框架的模板渲染的實(shí)現(xiàn),感興趣的可以了解一下2023-10-10go運(yùn)算符對(duì)變量和值執(zhí)行操作示例詳解
這篇文章主要為大家介紹了go運(yùn)算符對(duì)變量和值執(zhí)行操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09