Map源码解读--delete

270 阅读2分钟

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
}

\