数组、切片和映射
1.数组
对于数组的定义以及初始化,可以分为以下三种方式:
//1.先定义,再赋值
var list1 [8]string
list1[0] = "第一个值"
//没有初始化赋值的部分默认会用零值填充,string类型的零值为空的字符串
//2.赋值并初始化
var list2 = [8]string{"第一个值","第二个值"}
list3 := [8]string{"第一个值","第二个值"}
//3.不设置数组大小,让数组根据内容自动设置大小
var list4 = [...]string{"第一个值","第二个值"}
//等价于 var list4 = [2]string{"第一个值","第二个值"}
数组的循环变量,可以使用以下2种方式:
func main() {
var list = [8]string{"第一个值","第二个值"}
//最常见的循环方式
for i := 0;i < len(list);i++{
fmt.Println(list[i])
}
//使用range实现循环
for i,value := range list{
fmt.Printf("index :%d , value : %s\n" ,i,value)
}
}
对于数组来说,无论是将数组赋值给新的变量还是将它传递给函数,都会产生一个完整的数组副本。
所以传递数组给一个函数,在函数里面修改数组的值仅仅只是修改了原始数组的一个副本。
2.切片
切片使用:将元素进行区间取值。其中取值的数字代表的下标相当于前闭后开区间。
切片的如果是从0开始,':'前面的值也可以不写。如果取值范围是到结尾,那么':'后面的值可以不写。
切片里面元素值的改变是不会对切片数据原始来源的数据产生影响的。
func main() {
var list = [8]int{0,1,2,3,4,5,6,7}
//切片
list1 := list[0:4]
list2 := list[4:6]
list3 := list[6:8]
fmt.Println(list1,list2,list3)//打印结果为[0 1 2 3] [4 5] [6 7]
list5 := list[:4] //值的结果等价于list1
list6 := list[6:] //值的结果等价于list3
}
一般来说,函数传递不会传递数组,而是传递切片。
func main() {
var list = [8]int{0,1,2,3,4,5,6,7}
var list2 = [2]int{0,1}
var list3 = []int{0,1,2}
var list4 = []int{0,1,2,4}
//list 与 list2 是不同的类型,但是 list3 与 list4 是同一类型
setList(list3)
setList(list4)
}
func setList(num []int){
}
相比于有固定大小的数组,切片是一个可以根据需要,按需增长的。
这就涉及到切片的长度和容量了。
func main() {
var list = []int{0,1,2,4}
fmt.Printf("list 长度为 %d ,容量为 %d ,内容为 %v\n",len(list),cap(list),list)
//输出结果为
//list 长度为 4 ,容量为 4 ,内容为 [0 1 2 4]
list2 := list[1:3]
fmt.Printf("list2 长度为 %d ,容量为 %d ,内容为 %v",len(list2),cap(list2),list2)
//输出结果为
//list2 长度为 2 ,容量为 3 ,内容为 [1 2]
}
为什么list2输出的结果容量为3呢?
因为切片取值的时候,默认会切掉前面的部分,后面部分是隐藏的,虽然无法通过下标取值的方式取值到隐藏容量里面的内容,但是数据其实都还是存在的。
append函数
切片里面的内容可以通过append函数对数据进行追加。
每次超过数组容量以后,切片会自动扩充容量,容量为当前数组的2倍。并且扩容后的切片和原始的切片指向的是不同的数组。但是如果容量没有改变,那么他们指向的就是相同的数组,修改其中的数据会对同一容量的切片产生影响。
func main() {
var list = []int{0,1,2,4}
list2 := append(list,5)
list3 := append(list2,6,7)
fmt.Printf("list2 长度为 %d ,容量为 %d ,内容为 %v",len(list2),cap(list2),list2)
//输出结果为
//list2 长度为 5 ,容量为 8 ,内容为 [0 1 2 4 5]
fmt.Printf("list3 长度为 %d ,容量为 %d ,内容为 %v",len(list3),cap(list3),list3)
//输出结果为
//list3 长度为 7 ,容量为 8 ,内容为 [0 1 2 4 5 6 7]
list2[0] = -1
fmt.Println(list,list2,list3)
//输出结果为
//[0 1 2 4] [-1 1 2 4 5] [-1 1 2 4 5 6 7]
}
因为隐藏空间的内容不会被抹去,所以切片提供了一个三索引切分的方法,用来设置切片的后面的截取index在哪里。
func main() {
var list = []int{0,1,2,3,4,5,6,7,8,9}
list2 := list[1:5]
list3 := list[1:5:5]
fmt.Printf("list2 长度为 %d ,容量为 %d ,内容为 %v\n",len(list2),cap(list2),list2)
//输出结果为
//list2 长度为 4 ,容量为 9 ,内容为 [1 2 3 4]
fmt.Printf("list3 长度为 %d ,容量为 %d ,内容为 %v\n",len(list3),cap(list3),list3)
//输出结果为
//list3 长度为 4 ,容量为 4 ,内容为 [1 2 3 4]
}
make函数
使用make函数可以对数据进行初始化的长度和容量分配。好处是可以避免额外的内存分配和数组复制操作。
func main() {
var list = make([]int,0,10)
var list2 = make([]int,10)//这种初始化时默认会用类型的零值作为数据填充的
fmt.Printf("list 长度为 %d ,容量为 %d ,内容为 %v\n",len(list),cap(list),list)
//输出结果为
//list 长度为 0 ,容量为 10 ,内容为 []
fmt.Printf("list2 长度为 %d ,容量为 %d ,内容为 %v\n",len(list2),cap(list2),list2)
//输出结果为
//list2 长度为 10 ,容量为 10 ,内容为 [0 0 0 0 0 0 0 0 0 0]
}
3.映射(map)
map的声明方式
// 变量名 = map[key的类型] 值的类型 {}
var mapItem = map[int] string{}
简单的例子:
func main() {
// 变量名 = map[key的类型] 值的类型 {}
var mapItem = map[int] string{
1:"张三",
2:"李四",
}
fmt.Println(mapItem)
//输出: map[1:张三 2:李四]
}
如果访问的键不存在与map之中,那么go语言会根据值类型返回相应的零值。
当然对于这种情况,go语言也提供了方法供你进行判断是否存在再来处理,而不是直接输出零值。
func main() {
// 变量名 = map[key的类型] 值的类型 {}
var mapItem = map[int] string{
1:"张三",
2:"李四",
}
if key,has := mapItem[3];has{
fmt.Println(key)
}else {
fmt.Println("不存在")
}
}
map与其他普通的类型不同,map被赋值给其它变量或者传递给某个函数的参数时,对其中一个修改都会影响其它的map,因为它们共享底层数据。
map也可以使用make函数初始化分配空间。
网友评论