数据结构
数组
数组:相同元素,内存地址连续,利用索引可以快速访问到元素,容量确定(不可以扩容)的一种数据结构
初始化方式
1 2
| arr1 := [3]int{1, 2, 3} arr2 := [...]int{1, 2, 3}
|
访问和赋值
利用下标进行索引查询每个元素,对应元素可以进行赋值操作,但不能超出数组长度的索引查询请求。
1 2 3 4 5 6
| arr1 := [3]int{1, 2, 3} for i := range arr1 { arr1[i] = 3 } fmt.Println(arr1) fmt.Println(arr1[3])
|
切片
动态数组:长度是动态的,可以在容量不足时扩容。
切片其实内部引用的数组结构
数据结构
1 2 3 4 5
| type SliceHeader struct { Data uintptr Len int Cap int }
|
切片操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| slice := []int{1, 2, 3}
slice := make([]int, 3)
arr := [3]int{1, 2, 3} slice := arr[:]
for i := range slice { slice[i] = 1 }
for _, v := range slice { v = 1 }
slice = append(slice, 1)
arr := []int{} copy(arr, slice)
fmt.Println(len(slice)) fmt.Println(cap(slice))
|
切片扩容
切片是一个动态数组,容量不固定,所以当需要进行扩容时,会进行数组的复制,重新选择一个地址来存储数组。
扩容的具体策略分为:
- 当前期望容量大于当前容量两倍时,就会使用期望容量;
- 当前切片长度小于 1024 时,就会将容量翻倍;
- 当前切片长度大于 1024 时,就会每次按 25% 的容量扩容,直到新容量满足期望容量
哈希表
map 就是go 中应用 哈希表方式进行数据存储的结构。
map 采用 哈希桶 进行存储数据的方式,哈希桶的地址是连续的,分为正常桶 和 溢出桶 ,每个桶中数据通过 链表 存储当前 key 利用哈希函数生成的哈希值对应的数据。
一个正常桶存储 8个键值对,当 正常桶装满时,会存储到溢出桶。正常桶与溢出桶通过链表连接。
数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.POinter nevacuate uintptr extra *mapextra }
type mapextra struct { overflow *[]*bmap oldverflow *[]*bmap nextOverflow *bmap }
type bmap struct { tophash [bucketCnt]uint8 topbits [8]uint8 keys [8]keytype values [8]valuetype pad uintptr overflow uintptr }
|
map 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| hash := map[string]int{ "easy": 1, "mid": 2, "hard": 3, } hash := make(map[string]int)
for key, value := range hash { fmt.Println(key, value) hash[key] = 1 }
if v, ok := hash["ww"]; ok { fmt.Println(v, ok) } else { fmt.Println(v, ok) }
delete(hash, "easy")
|
map 的扩容
哈希扩容触发的两种情况:
字符串
字符串是go 中基础类型,可以当作一个由字符组成的数组,本身占用的内存空间是连续的。
数据结构
1 2 3 4
| type StringHeader struct { Data uintptr Len int }
|
字符操作
1 2 3 4 5 6 7 8 9 10
| str1 := "hello wolrd"
str2 := "!" str3 := str1 + str2
for i := range str1 { fmt.Println(str1[i]) }
|
Byte类型
byte 是一个 8 位无符号整数类型,底层是 uint8。值范围 0~ 255,可以表示 ASCII 字符集的单个字节。
在文件操作中,常用于读取和写入字节数据。
1 2 3 4 5 6 7 8
| b := 'A'
i:= int(b)
b2 := byte(65)
|
rune 类型
是 int32 类型的别名,表示一个 Unicode 字符。
可用于区分字符值和整数值。
每个 rune 占用 4 个字节。
1 2 3 4 5 6 7 8
| r := '呵呵'
isLetter := unicode.IsLetter(r)
i := int32(r)
|
语言特性
函数
特点
值传递
支持多返回值
函数入参和出参的内存空间都在栈上进行分配
1 2 3 4
| func hello(a int) (int, int) { return 1 + a, 2 }
|
接口
接口是一组方法的签名,
数据结构
关键字
for 和 range
select
defer
panic 和 recover
make 和 new
参考书籍
- Go语言设计与实现
- Go语言高级编程
- Go语言底层原理剖析