go map 的值传递
1 | package main |
在 Go 语言中,map 是引用类型,但在函数参数传递时,仍然是按值传递的。这意味着当你将一个 map 传递给函数时,传递的是 map 引用的副本,而不是原始的引用。因此,在函数内部对 map 引用本身的重新赋值不会影响到原始的 map。
解释
changeMapM 函数:
传递的是 map 引用的副本。
在函数内部,通过引用修改 map 中的值,这会影响到原始的 map,因为引用指向的是同一个底层数据结构。
changeMapN 函数:
传递的是 map 引用的副本。
在函数内部,重新赋值 n,这只是改变了 n 引用的副本指向一个新的 map,不会影响到原始的 map。
解释
changeMapM 函数:
m[“hello”] = 2:修改了 map 中的值,因为 m 引用指向的是原始的 map。
changeMapN 函数:
n = map[string]int{“hello”: 2}:重新赋值 n,使其指向一个新的 map,但这不会影响到原始的 map,因为 n 只是引用的副本。
总结
在 Go 语言中,map 是引用类型,但函数参数传递是按值传递的。
在函数内部对 map 引用本身的重新赋值不会影响到原始的 map。
要修改原始的 map,需要通过引用修改其内容,而不是重新赋值引用。
go map 存储
在 Go 语言中,map 的底层实现使用了桶(bucket)来存储键值对。当桶中的键值对数量超过一定限制时,会创建溢出桶(overflow bucket)。此外,当 map 中的键值对数量增加到一定程度时,map 会进行扩容,以保持较低的哈希冲突率和较高的性能。
创建溢出桶(overflow bucket)
溢出桶是在以下情况下创建的:
桶已满:每个桶有一个固定的容量(通常是 8 个键值对)。当一个桶中的键值对数量超过这个容量时,会创建一个溢出桶来存储额外的键值对。
哈希冲突:当多个键的哈希值映射到同一个桶时,会发生哈希冲突。如果桶已满且发生哈希冲突,会创建溢出桶来存储冲突的键值对。
扩容
map 会在以下情况下进行扩容:
负载因子超过阈值:负载因子是 map 中键值对的数量与桶的数量之比。当负载因子超过某个阈值(通常是 6.5)时,map 会进行扩容。扩容时,map 的桶数量会增加一倍,以减少哈希冲突并提高性能。
溢出桶过多:如果 map 中有太多的溢出桶,说明哈希冲突较多,map 也会进行扩容,以减少溢出桶的数量。
总结
溢出桶:当桶中的键值对数量超过容量或发生哈希冲突时,会创建溢出桶。
扩容:当负载因子超过阈值或溢出桶过多时,map 会进行扩容,以减少哈希冲突并提高性能。
这些机制确保了 map 在存储大量键值对时仍能保持较高的性能和较低的哈希冲突率。
参考
If a map isn’t a reference variable, what is it?
there-is-no-pass-by-reference-in-go
https://dave.cheney.net/2015/12/07/are-go-maps-sensitive-to-data-races