本地去重怎么实现?

87 阅读1分钟

给定一个数组 如何保证访问数组中的元素实现 有且仅有一次的语义

很容易想到使用一个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,
        },
    )
}