開發 hashset 經常使用的套路:javascript
map[int]int8
map[int]bool複製代碼
咱們通常只用 map 的鍵來保存數據,值是沒有用的。因此來緩存集合數據會形成內存浪費。java
空對象是個神奇的東西。它指的是沒有字段的結構類型。git
type Q struct{}複製代碼
它牛逼的地方在於:github
能夠和普通結構同樣操做緩存
var a = []struct{}{struct{}{}}
fmt.Println(len(a)) // prints 1複製代碼
不佔用空間性能
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0複製代碼
聲明兩個空對象,它們指向同一個地址學習
type A struct{}
a := A{}
b := A{}
fmt.Println(&a == &b) // prints true複製代碼
形成這個結果的緣由是 Golang 的編譯器會把這種空對象都當成runtime.zerobase
處理。優化
var zerobase uintptr複製代碼
有了上面的介紹,就能夠利用空結構來優化 hashset 了。ui
var itemExists = struct{}{}
type Set struct {
items map[interface{}]struct{}
}
func New() *Set {
return &Set{items: make(map[interface{}]struct{})}
}
func (set *Set) Add(item interface{}) {
set.items[item] = itemExists
}
func (set *Set) Remove(item interface{}) {
delete(set.items, item)
}
func (set *Set) Contains(item interface{}) bool {
if _, contains := set.items[item]; !contains {
return false
}
return true
}複製代碼
一個簡易的 hashset 實現就完成了。spa
func BenchmarkIntSet(b *testing.B) {
var B = NewIntSet(3)
B.Set(10).Set(11)
for i := 0; i < b.N; i++ {
if B.Exists(1) {
}
if B.Exists(11) {
}
if B.Exists(1000000) {
}
}
}
func BenchmarkMap(b *testing.B) {
var B = make(map[int]int8, 3)
B[10] = 1
B[11] = 1
for i := 0; i < b.N; i++ {
if _, exists := B[1]; exists {
}
if _, exists := B[11]; exists {
}
if _, exists := B[1000000]; exists {
}
}
}
BenchmarkIntSet-2 50000000 35.3 ns/op 0 B/op 0 allocs/op
BenchmarkMap-2 30000000 41.2 ns/op 0 B/op 0 allocs/op複製代碼