本文介绍: 如 B=5,那么用 64 位最后 5 位表示第几号桶,在用hash 值的高 8 位确定在 bucket 中的存储位置,当前 bmap 中的 bucket 未找到,则查询对应的 overflow bucket,对应位置有数据则对比完整的哈希值, 确定是否是要查找的数据。因为hash map 的内存是按照2的倍数开辟的,当前面开辟的内存不够的时候,会新开辟一段内存,将原来内存的数据转移到新的内存块中,这个过程是没有加锁的,如果这个时候同时有个读的线程过来获取这块内存数据,就会出现安全问题。
3. 数组和切片
3.1 数组和切片的区别
Go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小。
切片是一种数据结构,包含一个底层数组的指针,当前切片个数 len 以及切片的最大容量 cap, 描述的是一块数组。
3.2 切片的扩容策略
切片的扩容都是调用growslice方法,不同版本,扩容机制也有细微差距。
Go1.17 版本,切片在扩容时会进行内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于老 slice 容量的 2倍或者1.25倍。
当新切片需要的容量大于两倍扩容的容量,则直接按照新切片需要的容量扩容,当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。
在1.18时,又改成不和1024比较了,而是和256比较。
简单地说,就是小切片按照2倍扩容,大切片按照1.25倍扩容。
Go官方注释说这么做的目的是能更平滑的过渡。
3.3 Go 中对 nil 的 Slice 和空 Slice 的处理是一致的吗?
首先 Go 的 JSON 标准库对 nil slice 和 空 slice 的处理是不一致。
4. map
4.1 map 的底层实现
Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其 bucket)。
4.2 map 如何扩容
扩容其实就是一个预分配内容的过程,在 map 中的表现是 预分配 bucket。
4.3 什么时候会发生扩容
触发 map 扩容的时机:在向 map 插入新 key 的时候,会进行条件检测,符合下面这 2 个条件,就会触发扩容:
4.4 map 什么时候会发生迁移
4.5 map 查找
4.6 map 删除
4.7 为什么遍历map是无序的?
4.8 如何实现有序遍历map?
4.9 为什么Go map是非线程安全的?
4.10 线程安全的map如何实现?
4.11 Go sync.map 和原生 map 谁的性能好,为什么?
4.12 为什么 Go map 的负载因子是 6.5?
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。