本文已参与「新人创作礼」活动,一起开启掘金创作之路
引言
在golang中没有内置的set,我们可以使用以下两种方式实现
- 位图:数组,二进制位表示一个数是否存在。例如Byte 0-7位可以表示8个连续数字是否出现。
- map:
map[interface{}]bool
或者map[interface{}]struct{}
实现。
本文主要介绍这两种实现方式
1.位图(数组+位运算)
适用于集合中数字最大值确定的情况。
对于一个数据集合nums,若其最大值为max,一个Byte可以表示8个数,那么可以用max/8 +1 个byte表示0-max的数是否存在。
package main
import "fmt"
// BitMap 位图
type BitMap struct {
bits []byte
max int
}
// NewBitMap 初始化一个BitMap
// 一个byte有8位,可代表8个数字,max取模后加1为存放最大数所需的容量
// >>3等价于除以8
func NewBitMap(max int) *BitMap {
bits := make([]byte, (max>>3)+1)
return &BitMap{bits: bits, max: max}
}
// Add 添加一个数字到位图
//计算添加数字在数组中的索引index,一个索引可以存放8个数字
//计算存放到索引下的第几个位置,一共0-7个位置
//原索引下的内容与1左移到指定位置后做或运算
func (b *BitMap) Add(num uint) {
index := num >> 3
pos := num & 0x07
b.bits[index] |= 1 << pos
}
// Contains 判断一个数字是否在位图
//找到数字所在的位置,然后做与运算
func (b *BitMap) Contains(num uint) bool {
index := num >> 3
pos := num & 0x07
return b.bits[index]&(1<<pos) != 0
}
// Remove 删除一个数字在位图
//找到数字所在的位置取反,然后与索引下的数字做与运算
func (b *BitMap) Remove(num uint) {
index := num >> 3
pos := num & 0x07
b.bits[index] = b.bits[index] & ^(1 << pos)
}
// Max 位图的最大数字
func (b *BitMap) Max() int {
return b.max
}
func (b *BitMap) String() string {
return fmt.Sprint(b.bits)
}
func main() {
bitmap := NewBitMap(32)
bitmap.Add(1)
bitmap.Add(3)
bitmap.Add(5)
fmt.Println(bitmap.String())
}
2.map[interface{}]struct{}实现
使用map[interface{}]struct{}
实现,可以支持任意类型,并且struct{}不占内存。
但是我个人觉得没有必要做set结构的封装,其本质还是使用map进行实现,所以推荐大家在使用时还是直接使用map[interface{}]struct{}
,set需要的操作用map完全够用。
package main
import (
"fmt"
"unsafe"
)
//使用空结构体大小0,不占内存
type exists struct{}
//使用空接口interface{},支持任意类型元素
type set struct {
m map[interface{}]exists
}
// NewSet 调用Add函数添加多个元素到set中
func NewSet(items ...interface{}) *set {
s := &set{}
s.m = make(map[interface{}]exists)
s.Add(items...)
return s
}
// Add 将元素列表插入到set中
func (s *set) Add(items ...interface{}) {
for _, item := range items {
s.m[item] = exists{}
}
}
// Remove 删除指定元素
func (s *set) Remove(item interface{}) {
delete(s.m, item)
}
// Contains 查询指定元素是否存在
func (s *set) Contains(item interface{}) bool {
_, ok := s.m[item]
return ok
}
// Size 查询集合大小
func (s *set) Size() int {
return len(s.m)
}
func main() {
s1 := struct{}{}
s2 := struct{}{}
fmt.Printf("s1 address:%p, s2 address:%p\n", &s1, &s2) //地址一样
fmt.Println(s1 == s2) //true
fmt.Println(unsafe.Sizeof(struct{}{})) //0
s := NewSet(8, "beijing")
arr := [5]int {1,2,3,4,5}
s.Add(arr)
s.Add(false)
fmt.Println(s.Contains(false)) //true
fmt.Println(s.Contains(8)) //true
fmt.Println(s.Contains(arr)) //true
}