Map源码解读--delete
直接上源码,mapdelete函数
func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
//一些安全检查,略过不看
/**if raceenabled && h != nil {
callerpc := getcallerpc()
pc := abi.FuncPCABIInternal(mapdelete)
racewritepc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc)
}
if msanenabled && h != nil {
msanread(key, t.key.size)
}
if asanenabled && h != nil {
asanread(key, t.key.size)
}**/
//map是nil或者没有元素,直接返回
if h == nil || h.count == 0 {
if t.hashMightPanic() {
t.hasher(key, 0)
}
return
}
//检查并发写
if h.flags&hashWriting != 0 {
throw("concurrent map writes")
}
//计算对应key的hash值
hash := t.hasher(key, uintptr(h.hash0))
//标记写
h.flags ^= hashWriting
//选桶
bucket := hash & bucketMask(h.B)
//判断是否正在扩容,若是,加入搬迁(goland的rehash机制)
if h.growing() {
growWork(t, h, bucket)
}
//获取对应的bmap
b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize)))
bOrig := b
//获取高八位hash
top := tophash(hash)
search:
//遍历bmap到溢出桶,先找到key对应的位置
for ; b != nil; b = b.overflow(t) {
//遍历当前bmap
for i := uintptr(0); i < bucketCnt; i++ {
//先根据高八位进行判断
if b.tophash[i] != top {
//表示后续都是空的,无需再遍历下去了
if b.tophash[i] == emptyRest {
break search
}
continue
}
//高八位相同,取出对应的位置的key,相比较是否相同
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
k2 := k
if t.indirectkey() {
k2 = *((*unsafe.Pointer)(k2))
}
if !t.key.equal(key, k2) {
//不相等,继续遍历,找下一个
continu
}
// 判断key是不是指针类型
if t.indirectkey() {
//指针类型,改引用为nil
*(*unsafe.Pointer)(k) = nil
} else if t.key.ptrdata != 0 {
//key类型包含指针变量,回收key的内存
memclrHasPointers(k, t.key.size)//从k开始,回收size的内存
}
//key对应的value的地址
e := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))
//判断value类型,选择回收方法
if t.indirectelem() {
*(*unsafe.Pointer)(e) = nil
} else if t.elem.ptrdata != 0 {
memclrHasPointers(e, t.elem.size)
} else {
memclrNoHeapPointers(e, t.elem.size)
}
//标记改位置未空
b.tophash[i] = emptyOne
//i是在删除的key在数组里的下标
if i == bucketCnt-1 {
//如果i是数组尾,且存在存有元素的溢出数组
if b.overflow(t) != nil && b.overflow(t).tophash[0] != emptyRest {
goto notLast
}
} else {
//该位置后还有其它元素
if b.tophash[i+1] != emptyRest {
goto notLast
}
}
for {
//没有通过goto而是来到这里,说明这个元素后都是空闲的
//设置当前位置为emptyRest,遍历的话到这里就可以停了
b.tophash[i] = emptyRest
//如果删的数组开头
if i == 0 {
//bOrig在开始找bmap时赋值的
//如果b还是原来的值,也就是没走到溢出桶
if b == bOrig {
break
}
//找到这个溢出桶的前一个bmap,i走到数组末尾
c := b
for b = bOrig; b.overflow(t) != c; b = b.overflow(t) {
}
i = bucketCnt - 1
} else {
i--
}
if b.tophash[i] != emptyOne {
break
}
//这个for循环其实就是标记emptyRest用的
}
notLast:
//数量减一
h.count--
//如果空了,重新设置hash种子,让map更安全
if h.count == 0 {
h.hash0 = fastrand()
}
break search
}
}
//取消写标记
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
h.flags &^= hashWriting
}
\