大家都知道可以使用make方法来创建map类型,对比创建 slice 类型,创建map是否也需要指定len和cap两个参数呢?

如果map要容纳的数据比较多,其实是需要指定len属性的,我们可以从创建map的源码中了解到(本文都采用go1.18的源码):

makemap
通过源码注释我们可以了解到,hint 就是make创建map指定的第二个参数。那么,这个参数如何决定map内部实际存储的大小呢?

通过 hint来计算 B 的值,B作为map结构体中的一个属性,用来间接标识容量的数量。map最多可以容纳 负载因子 乘以 2的B次幂的数据。当然,我们看看这个 for 循环退出的条件是什么?

overLoadFactor
下面是函数overLoadFactor的源码,结合外面的for 循环,通过不断的B++,最终计算结果大于 hint 就会退出循环。其中,bucketCnt 是一个值为8的常量,所以,我们make指定len 的时候,如果值小于8就别指定了。然后就是与运算符右边的表达式了。

bucketShift(B)
// overLoadFactor reports whether count items placed in 1<<B buckets is over loadFactor.
func overLoadFactor(count int, B uint8) bool {
	return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)
}

现在通过 hint 获取到了B值,也就是通过make方法的len参数计算到了B值,准确的说,B在map中用来标志 bucket 的个数。

makeBucketArray

makeBucketArraty
这里假设参数 b 等于4,那么base等于2的4次幂,值为16。考虑到可能出来溢出,会考虑多加1个bucket,所以,nbuckets 现在等于 16 + 1 了。然后我们就可以计算并申请空间了。

在这里插入图片描述

计算申请所需空间的函数也可以了解一下,Go 将申请的空间预设成了不同规格的大小块,我们需要要在申请的空间能被这些块覆盖。大概就跟寄快递一样,快递的纸箱盒子都是预设好的,有不同规格的大小,我们要邮寄的东西必须选择最合适的那个纸箱

在这里插入图片描述
总结:

如果map要存储的数据比较多,在make创建map的时候,非常有必要指定参数len