type slice struct {
array unsafe.Pointer
len int
cap int
}
array
:引用的底层数组,动态数组,可以修改
len
::长度cap
:可以理解为底层动态数组的容量
扩容后array指针指向可能会发生更改
package main
import "fmt"
func main() {
slice1 := make([]int, 0, 4)
fmt.Println(len(slice1))
// panic
slice1[0] = 1
}
package main
import "fmt"
func main() {
slice1 := make([]int, 4, 8)
// 4 8
fmt.Println(len(slice1), " ", cap(slice1))
slice2 := slice1[2:]
// 2 6
fmt.Println(len(slice2), " ", cap(slice2))
}
当底层动态数组只要有一个切片引用,则整个动态数组就不会被回收,即使这个切片引用的是动态数组的局部
package main
import "fmt"
func main() {
s1 := make([]int, 4, 8)
fmt.Printf("s1的指针是 %p \n", &s1)
printPoint(s1)
fmt.Printf("s1的底层数组是 %p \n", s1)
printArrPoint(s1)
}
func printPoint(s2 []int) {
fmt.Printf("s2的指针是 %p \n", &s2)
}
func printArrPoint(s2 []int) {
fmt.Printf("s2的底层数组是 %p \n", s2)
}
//s1的指针是 0xc000008078
//s2的指针是 0xc000008090
//s1的底层数组是 0xc000014240
//s2的底层数组是 0xc000014240
package main
import "fmt"
func main() {
s1 := make([]int, 4, 4)
s2 := s1
fmt.Printf("s1的底层数组是 [%p], 容量是 [%v]\n", s1, cap(s1))
fmt.Printf("s2的底层数组是 [%p], 容量是 [%v]\n", s2, cap(s2))
s1 = append(s1, 1)
fmt.Printf("s1的底层数组是 [%p], 容量是 [%v]\n", s1, cap(s1))
fmt.Printf("s2的底层数组是 [%p], 容量是 [%v]\n", s2, cap(s2))
}
//s1的底层数组是 [0xc000150020], 容量是 [4]
//s2的底层数组是 [0xc000150020], 容量是 [4]
//s1的底层数组是 [0xc0001200c0], 容量是 [8]
//s2的底层数组是 [0xc000150020], 容量是 [4]
package main
import "fmt"
func main() {
var a []int
b := make([]int, 0)
fmt.Printf("a==nil? %v \n", a == nil)
fmt.Printf("b==nil? %v \n", b == nil)
fmt.Printf("len(a)==0? %v \n", len(a))
fmt.Printf("len(b)==0? %v \n", len(b))
fmt.Printf("a的指针是 [%p]\n", &a)
fmt.Printf("b的指针是 [%p]\n", &b)
fmt.Printf("a的底层数组是 [%p]\n", a)
fmt.Printf("b的底层数组是 [%p]\n", b)
}
//a==nil? true
//b==nil? false
//len(a)==0? 0
//len(b)==0? 0
//a的指针是 [0xc000008078]
//b的指针是 [0xc000008090]
//a的底层数组是 [0x0]
//b的底层数组是 [0xe6b438]
type hmap struct {
count int // map 中键值对的数量
flags uint8 // map 的标志位,如是否为引用类型等
B uint8 // map 的桶大小的对数
noverflow uint16 // 溢出桶的数量
hash0 uint32 // 哈希种子值
buckets unsafe.Pointer // 存储桶的指针
oldbuckets unsafe.Pointer // 旧桶的指针,用于扩容时的过渡
nevacuate uintptr // 扩容时,已迁移的桶的数量
extra *mapextra // 用于存储特殊情况下的扩展信息
}
type mapextra struct {
overflow *[]*bmap // 溢出桶的数组,当哈希表中的键值对数量超过某个阈值时会使用溢出桶
oldoverflow *[]*bmap // 旧溢出桶的数组,用于扩容时的过渡
nextOverflow *bmap // 链接下一个溢出桶的指针
}
type bmap struct {
tophash [bucketCnt]uint8
}
//在编译期间会产生新的结构体
type bmap struct {
tophash [8]uint8 //存储哈希值的高8位
keys [8]keytype //key数组
values [8]valuetype // value数组
pad uintptr
overflow *bmap //溢出bucket的地址
}
起到一种加速的作用
),找到对应的下标i。过程跟get差不多
),如果找到了,则替换valuei
,要么就是i+len(原来桶数组)
ladFactor=count/(2^B)
)package main
import "fmt"
func main() {
a := make(map[string]int, 0)
// false
fmt.Println(a == nil)
var b map[string]int
// true
fmt.Println(b == nil)
}
参考:https://juejin.cn/post/7029679896183963678#heading-1