映射
映射是一种用来存储一系列无序键值对的数据结构
- 映射的底层存储结构。
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
// mapextra holds fields that are not present on all maps.
type mapextra struct {
// If both key and elem do not contain pointers and are inline, then we mark bucket
// type as containing no pointers. This avoids scanning such maps.
// However, bmap.overflow is a pointer. In order to keep overflow buckets
// alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow.
// overflow and oldoverflow are only used if key and elem do not contain pointers.
// overflow contains overflow buckets for hmap.buckets.
// oldoverflow contains overflow buckets for hmap.oldbuckets.
// The indirection allows to store a pointer to the slice in hiter.
overflow *[]*bmap
oldoverflow *[]*bmap
// nextOverflow holds a pointer to a free overflow bucket.
nextOverflow *bmap
}
// A bucket for a Go map.
type bmap struct {
// tophash generally contains the top byte of the hash value
// for each key in this bucket. If tophash[0] < minTopHash,
// tophash[0] is a bucket evacuation state instead.
tophash [bucketCnt]uint8
// Followed by bucketCnt keys and then bucketCnt elems.
// NOTE: packing all the keys together and then all the elems together makes the
// code a bit more complicated than alternating key/elem/key/elem/... but it allows
// us to eliminate padding which would be needed for, e.g., map[int64]int8.
// Followed by an overflow pointer.
}
可以看出一个映射里包含了很多个hash桶(bucket),每个桶里存储了hash值的高8位以及8个key和8个value,key的hash值的低8位确定key在桶里的位置。每次查找,删除键值对的时候都把需要把key传给hash函数,由hash函数生成一个索引,这个索引最终将键值对分布到所有可用的桶里,具体的底层数据结构另说。
- 创建初始化使用
dict := make(map[string]int) //make函数创建一个无须集合。
dict := map[string][int]{"chujiu":20,"dong":30} //字面量初始化一个映射
dict1 := map[string][int]{}//初始化一个空的map
dict := map[[]string]int{} //这会抛出一个错误,map的key理论上可以用任何类型作为key,但有引用类型除外,基本上可以做==等值比较的类型都可以作为key,这里用切片作为key是不行的,引用,函数,包含切片的结构类型都不行。
//Invalid map key type: the comparison operators == and != must be fully defined for key type
var dict2 map[string]int //声明一个为nil的映射,这和空的映射并不相等。也不能赋值
dict2["age"] = 2 //针对nil的映射存储值会发生一个panic错误。 assignment to entry in nil map
dict1["age"] = 2 //这个是可以的,初始化的时候是一个空的映射。
dict2 = dict1 //这样也是可以的。但这是一个引用操作,对dict2操作任何修改dict1都会受到影响。因为指向的是同一个bucket数组。
//从映射中取出一个值来,即使这个key不存在,也会返回一个对应的零值。
value := dict1["num"] //即使key不存在,value也会返回一个0
if value == 0 {
fmt.Print(value)
}
for key, value := range dict1 {
fmt.Println("key:"+key, ",value:", value)
}
delete(dict1, "age")//删除一个映射中的key,没有返回值,函数间传递是引用传递,并不会拷贝一个新的映射集合,也就是当在函数内部对映射做出了修改后,原来的映射也会受到影响。
网友评论