// 迭代器中使用 funcmapaccessK(t *maptype, h *hmap, key unsafe.Pointer)(unsafe.Pointer, unsafe.Pointer){}
// 不返回 bool funcmapaccess1(t *maptype, h *hmap, key unsafe.Pointer)unsafe.Pointer {} funcmapaccess1_fat(t *maptype, h *hmap, key, zero unsafe.Pointer)unsafe.Pointer {}
// 返回 bool funcmapaccess2(t *maptype, h *hmap, key unsafe.Pointer)(unsafe.Pointer, bool) {} funcmapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer)(unsafe.Pointer, bool) {}
这些方法有很大的相关性,下面我们逐一来学习吧。
mapaccess1_fat, mapaccess2_fat
这两个方法,从字面上来看多了个fat,就是个宽数据。何以为宽,我们从下面代码找到原因:
1 2 3 4 5 6 7 8
//src/cmd/compile/internal/gc/walk.go
if w := t.Elem().Width; w <= 1024 { // 1024 must match runtime/map.go:maxZero n = mkcall1(mapfn(mapaccess1[fast], t), types.NewPtr(t.Elem()), init, typename(t), map_, key) } else { z := zeroaddr(w) n = mkcall1(mapfn("mapaccess1_fat", t), types.NewPtr(t.Elem()), init, typename(t), map_, key, z) }
if h == nil || h.count == 0 { // map 为空,或者size 为 0, 直接返回 } if h.flags&hashWriting != 0 { // 这里会检查是否在写,如果在写直接panic throw("concurrent map read and map write") }
// 拿到对应key 的hash,以及 bucket alg := t.key.alg hash := alg.hash(key, uintptr(h.hash0)) m := bucketMask(h.B) b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize))) if c := h.oldbuckets; c != nil { if !h.sameSizeGrow() { // There used to be half as many buckets; mask down one more power of two. m >>= 1 } oldb := (*bmap)(unsafe.Pointer(uintptr(c) + (hash&m)*uintptr(t.bucketsize))) if !evacuated(oldb) { b = oldb } }
// 获取tophash 值 top := tophash(hash) bucketloop: // 遍历解决冲突的链表 for ; b != nil; b = b.overflow(t) { // 遍历每个bucket 上的kv for i := uintptr(0); i < bucketCnt; i++ { // 先匹配 tophash // ...
// 获取k k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize)) if t.indirectkey() { k = *((*unsafe.Pointer)(k)) }
// 判断k是否相等,如果相等直接返回,否则继续遍历 if alg.equal(key, k) { v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize)) if t.indirectvalue() { v = *((*unsafe.Pointer)(v)) } return v, true } } } return unsafe.Pointer(&zeroVal[0]), false }