对于phper来说map,数组都是array,今天我们我们一起研究下go的array,slice,map之间的区别与联系
数组 array
可以存储相同类型的元素的固定大小顺序集合。数组的每个元素在内存中都是连续存放的,每个元素都有一个下标,下标从0开始。数组长度可以省略,会自动根据{}中的元素来进行推导。没有初始化的索引,默认值是数组类型的零值。数组用于存储数据集合,但将数组视为同一类型的变量的集合通常更有用。
定义
要在Go中声明一个数组,需要指定元素的类型常用声明方式:
//var <var_name> = [<len>]<type>{val1,val2}
var arr = [2]int{1,2}
arr := [2]int{1,2}
//var <var_name> = [...]<type>{val1,val2}
var arr2 = [...]int{1,2}
arr2 := [...]int{1,2}
//var <var_name> = [...]<type>{k1:v1,k2:v2}
var arr3 = [...]int{1:1,0:2}
arr3 := [...]int{1:1,0:2}
运用示例
//分数排名前五
orderScore:= [3]int{325,312,301,288,283,100}
//第一名分数
cham := orderScore[0]
//修改第三名的分数
orderScore[2] = 302
切片 slice
Go切片(Slice)是Go数组的一个抽象。可以理解为动态的数组,切片是基于数组实现的,它的底层就是一个数组。对于数组的分割,便可以得到一个切片。eg:array[start:end] 可以得到包括start下标到end(不包括end下标元素) 的子数组。
由于Go数组允许定义类型的变量,可以容纳相同数据类型的几个数据项,但它不提供任何内置的方法来动态增加其大小或获取自己的子数组。切片就没有这样的限制。
定义
要定义切片,可以将其声明为数组,而不指定大小或使用make函数创建一个。
//声明一个元素类型为int的切片,长度是4
numbers:= make([]int,5)
//长度是45,容量是5
numbers1:= make([]int,5,5)
运用示例
package main
import "fmt"
func main() {
//如果缺省情况下声明没有输入切片,则将其初始化为nil
var number []int
fmt.Printf("len=%d cap=%d slice=%v\n", len(number), cap(number), number) //len=0 cap=0 slice=[]
//创建容量与长度为5的切片
var numbers = make([]int, 3, 5)
//通过len,cap 获取长度 容量
fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers), cap(numbers), numbers) //len=3 cap=5 slice=[0 0 0]
numbers = []int{0, 1, 2, 3}
fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers), cap(numbers), numbers) //len=4 cap=4 slice=[0 1 2 3]
//append 添加元素,扩容为原来的两倍,numbers已经另起炉灶,numbers 与numbers1不共用array
numbers1 := append(numbers, 4, 5)
fmt.Printf("len=%d cap=%d slice number1=%v slice number=%v\n", len(numbers1), cap(numbers1), numbers1, numbers) //len=6 cap=8 slice number1=[0 1 2 3 4 5] slice number=[0 1 2 3]
//修改numbers1 不影响 numbers
numbers1[1] = 100
fmt.Printf("len=%d cap=%d slice number1=%v slice number=%v\n", len(numbers1), cap(numbers1), numbers1, numbers) //len=6 cap=8 slice number1=[0 100 2 3 4 5] slice number=[0 1 2 3]
//子切片 遵循左闭右开 从1的位置截取到4不包括4两个元素,从1截取到5,不包括5作为容量
numbers2 := numbers1[1:4:6]
fmt.Printf("len=%d cap=%d slice number2=%v slice number1=%v\n", len(numbers2), cap(numbers2), numbers2, numbers1) //len=3 cap=5 slice number2=[100 2 3] slice number1=[0 100 2 3 4 5]
//numbers2 与 numbers1 共用array 修改下标为0的元素,实际修改为底层的array,所以numbers1也同时变更number1的1对应number2的0下标
numbers2[0] = 200
fmt.Printf("len=%d cap=%d slice number2=%v slice number1=%v\n", len(numbers2), cap(numbers2), numbers2, numbers1) //len=3 cap=5 slice number2=[200 2 3] slice number1=[0 200 2 3 4 5]
//number2 追加数字6,因为number2的长度为5,容量够用,元素追加到末尾,下标为3,number2 的3下标对应number1的4的位置
numbers2 = append(numbers2, 6)
fmt.Printf("len=%d cap=%d slice number2=%v slice number1=%v\n", len(numbers2), cap(numbers2), numbers2, numbers1) //len=4 cap=5 slice number2=[200 2 3 6] slice number1=[0 200 2 3 6 5]
//append 追加超过numbers2的容量,创建新的底层数组,不会影响到源numbers1
numbers2 = append(numbers2, 7, 8)
fmt.Printf("len=%d cap=%d slice number2=%v slice number1=%v\n", len(numbers2), cap(numbers2), numbers2, numbers1) //len=6 cap=10 slice number2=[200 2 3 6 7 8] slice number1=[0 200 2 3 6 5]
//切片循环
for k, v := range numbers2 {
fmt.Printf("key:%d,value:%d\n", k, v)
//key:0,value:200
//key:1,value:2
//key:2,value:3
//key:3,value:6
//key:4,value:7
//key:5,value:8
}
}
映射 map
map 是一个无序的 k-v 键值对集合。 给定一个键和一个值就可以在Map对象中设置值。设置存储值后,就可以使用其键检索它对应的值了。其中 k 必须是相同类型。k 和 v 的类型可以不同。k 的类型必须支持 == 比较运算符,这样才可以判断它是否存在,并保证唯一
定义
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用
make(map[KeyType]ValueType, [cap])
应用示例
package main
import "fmt"
func main() {
//声明容量未200的map,该参数虽然不是必须的,其内部使用散列表(hash)实现,我们应该在初始化map的时候就为其指定一个合适的容量
addBook := make(map[string]string, 200)
addBook["李白"] = "13577785896"
addBook["亚瑟"] = "13566698745"
addBook["兰陵王"] = "13566698766"
fmt.Println(addBook)
fmt.Printf("type of a:%T\n", addBook)
//直接声明
userInfo := map[string]int{
"李白": 29,
"亚瑟": 18,
}
fmt.Println(userInfo)
//查找
libai := addBook["李白"]
fmt.Println(libai)
//修改
addBook["李白"] = "13566696969"
fmt.Println(addBook)
//删除
delete(addBook,"兰陵王")
fmt.Println(addBook)
//len 获取map 长度
fmt.Println(len(addBook))
//判断是否存在
v, ok := addBook["杜甫"]
if ok {
fmt.Println(v)
} else {
fmt.Println("不在这个峡谷!")
}
//map[亚瑟:13566698745 兰陵王:13566698766 李白:13577785896]
//type of a:map[string]string
//map[亚瑟:18 李白:29]
//13577785896
//map[亚瑟:13566698745 兰陵王:13566698766 李白:13566696969]
//map[亚瑟:13566698745 李白:13566696969]
//2
//不在这个峡谷!
for k,v := range addBook {
fmt.Printf("key:%s,value:%s\n",k,v)
//key:亚瑟,value:13566698745
//key:李白,value:13566696969
}
//map 嵌套多种数据类型
data := map[string]interface{}{
"user_name": "小明",
"avatar": "/avatar.jpg",
"order": map[string]interface{}{
"id": 111,
"order_sn": "sn4585448555",
"phone": 13577789996,
},
}
fmt.Println(data)
//map[avatar:/avatar.jpg order:map[id:111 order_sn:sn4585448555 phone:13577789996] user_name:小明]
总结
- slice 的底层数据是数组,slice 是对数组的封装,它描述一个数组的片段。两者都可以通过下标来访问单个元素。数组是定长的,长度定义好之后,不能再更改。在 Go 中,数组是不常见的,因为其长度是类型的一部分,限制了它的表达能力,比如 [3]int 和 [4]int 就是不同的类型。而切片则非常灵活,它可以动态地扩容。切片的类型和长度无关。数组就是一片连续的内存, slice 实际上是一个结构体,包含三个字段:长度、容量、底层数组
- 还可以深入研究map 实现原理,扩容过程