给定一个数组 如何保证访问数组中的元素实现 有且仅有一次的语义
很容易想到使用一个map记录对应的访问状态。那么还有没有比map占用空间更小的实现方式呢?并发访问如何保证安全呢?
基本数据结构
- l 表示容量大小
- b 数组 记录是否访问的状态
type EasyBloom struct {
l uint64
b []uint64
}
简单实现
const (
wordMark = 6
wordFlag = 1<<wordMark - 1 // 64
)
func NewEasyBloom(size uint64) *EasyBloom {
return &EasyBloom{
l: size,
b: make([]uint64, size>>wordMark+1),
}
}
func (e *EasyBloom) TestAndSet(idx uint64) (exist bool) {
if e == nil || idx >= e.l {
return false
}
for {
old := atomic.LoadUint64(&e.b[idx>>wordMark])
// 已经存在 直接返回
if old&getBitFlag(idx) != 0 {
return true
}
if atomic.CompareAndSwapUint64(&e.b[idx>>wordMark], old, old|getBitFlag(idx)) {
// 成功写入
return false
}
}
}
func (e *EasyBloom) Exist(idx uint64) bool {
return atomic.LoadUint64(&e.b[idx>>wordMark])&getBitFlag(idx) != 0
}
func (e *EasyBloom) Clear() {
if e == nil {
return
}
for i := range e.b {
e.b[i] = 0
}
}
func getBitFlag(idx uint64) uint64 {
return 1 << (idx & wordFlag)
}
序列化方法
type easyBloomMarshal struct {
L uint64 `json:"l"`
B []uint64 `json:"b"`
}
func (e *EasyBloom) UnmarshalJSON(data []byte) error {
var easyBloomMarshal *easyBloomMarshal
if err := json.Unmarshal(data, &easyBloomMarshal); err != nil {
return err
}
e.b = easyBloomMarshal.B
e.l = easyBloomMarshal.L
return nil
}
func (e *EasyBloom) MarshalJSON() ([]byte, error) {
return json.Marshal(
&easyBloomMarshal{
L: e.l,
B: e.b,
},
)
}