文章目录
- 写在前面
- 数组
- 切片
- Map
写在前面
前面的文章介绍了Go的一些基本类型,本文开始涉及Go的一些容器类型,它们都是可以包含多个元素的数据结构,如数组、切片、map
数组
数组是具有相同类型且长度固定的一组元素集合,定义的格式:
var 数组名 [数组长度]数组元素类型
,下面声明一个长度为5的int型数组
arr
- 数组元素可以是任意的原始类型,例如整型、字符串等,一个数组内所有元素的类型必须是相同的
- 数组的长度必须是一个常量表达式,且是一个非负整数,特别注意数组的长度也是数组类型的一部分,因此不同长度的数组是不同的类型。数组的长度在声明时就要给出。获取数组长度用内置函数
len(arr)
- 当声明数组时所有的元素都会被初始化为默认的类型零值,我们也可以在声明数组的同时进行初始化
还可以在初始化时仅初始化指定元素var arr = [5]int{1, 2, 3, 4, 5} a := [3]int{1, 2, 3}
- 如果声明数组时不想直接写长度,可以用
代替,编译器会自动生成满足最低长度要求的数组...
- 通过
循环来遍历操作数组for
var arr [5]int for i := 0; i < len(arr); i++ { arr[i] = i * 2 } for i, v := range arr { fmt.Println("index: ", i, "value: ", v) }
- 数组是一种值类型(不像C/C++那样是指向首元素的指针),可以通过
来创建数组,不过它返回的是指向该数组的指针new
注意区分指向数组的指针和指针数组d := new([4]int) fmt.Println(d) //&[0,0,0,0] fmt.Println(*d) //[0,0,0,0]
e := [5]int{1, 2, 3, 4, 5} var p *[5]int = &e //p为指向数组e的指针 &[1,2,3,4,5] x, y := 1, 2 z := [2]*int{&x,&y} //z是一个指针数组
- 多维数组
h := [2][3]int{ {1, 1, 1}, {2, 2, 2} }
- 数组可以使用
或==
来比较!=
f := [2]int{1, 2} g := [2]int{1, 2} fmt.Println(f == g) //true
切片
切片(slice)是对某个数组的一段连续片段的引用(该数组我们称为相关数组,该片段可以是整个数组,也可以是数组中的某一段),因此切片是一个引用类型。切片底层实现是数组,它与数组的关系如下图:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90zdOBTRE10dFRUYoh2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwgjN1QTOxYTM1ETNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
多个slice可以指向同一个底层相关数组,此时其中一个值改变会影响全部的值
- 切片是一个可变长的数组,它的长度可以动态修改,可以用
来获取切片的长度。切片还有另外一个属性容量,表示切片可以达到的最大长度,可以用len()
来获取。切片的容量等于切片的长度+相关数组中在切片之后剩下的的长度。所以切片的长度小于等于切片的容量。cap()
- 切片的容量是预先分配的,如果在运行的过程中,切片的长度超过了原来分配的容量,则会重新分配一个空间更大的数组,然后将值都拷贝过去
- 切片的声明格式:
,这里不需要指定长度,如果是数组则必须指定长度或者用var 切片名 []元素类型
替代...
- 切片可以通过底层相关数组切取,也可以用
直接创建,还可以通过已有的切片来生成make
- 通过数组生成
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} s1 := arr[2:5] //切取数组arr索引从2~4这一段(即[3,4,5]),注意索引值不包括5 s2 := arr[:] //切片为整个arr数组 s3 := arr[5:] //切取从索引5开始到末尾,即[6,7,8,9,10]
- 通过
make
创建
格式:
,其中make([]type, len, cap)
可以省略,省略则默认和cap
相同len
s4 := make([]int, 3, 20) s5 := make([]int, 3)
- 通过切片生成,这种方式有时也被称之未称未切片重组reslice
需要注意的是,新的切片的索引不能超过原切片的容量,否则会引发编译错误,而不是重新分配数组sa := []int{1, 2, 3, 4, 5} sb := sa[1:4] //[2,3,4],len:3,cap:4 sc := sb[1:4] //[3,4,5],len:3,cap:3
- 通过数组生成
-
在切片上追加元素(append)
如果要在切片上添加新的元素,可以使用
函数,用法为append
append(被追加元素的slice, 追加的元素...)
注意,如果追加后的长度未超过原slice的容量,则返回原始的slice,如果超过了,则重新分配空间更大的数组并拷贝原始数据s1 := make([]int, 3, 6) s1 = append(s1, 1, 2, 3) //[0,0,0,1,2,3],还是返回原来的slice (没有超过原来cap) s1 = append(s1, 1, 2, 3) //[0,0,0,1,2,3,1,2,3],返回的是一个新的slice(已经超过了cap,重新分配底层数组)
-
拷贝slice
可以使用
函数将一个切片拷贝到另外一个切片中,用法copy
copy(目标slice, 被拷贝的slice)
sc1 := []int{1, 2, 3, 4, 5} sc2 := []int{6, 7, 8} copy(sc1, sc2) fmt.Println(sc1) //[6 7 8 4 5] copy(sc2,sc1) fmt.Println(sc2) //[6,7,8] copy(sc2[0:2], sc1[3:5]) //指定具体位置 fmt.Println(sc2) //[4 5 8]
Map
Map是Go里面的键值对集合,由key-value对组成,给定key,可以快速定位到对应的value。也被称为字典、哈希表等
- map中的key可以是任意能够用
或者==
操作符比较的类型,比如!=
,string
、int
等,不能是函数、map、切片;value可以是任意类型float
- map的声明方式
var map变量名 map[key类型]vlaue类型
特别注意,map必须要初始化才能使用,即如果用上面这种方式,必须要有初始化的语句var m map[int]string //声明 m = map[int]string{1:"a", 2:"b"} //初始化
,否则将报错。当然如果觉得太繁杂,可以使用下面的map1 = map[keyType]ValueType{}
语句来替代,更加简洁(主要是因为使用前需要先分配好内存空间给map,使用初始化语句或者make
语句才能实现空间的分配)make
- map也属于引用类型,声明的时候不需要知道长度,可以动态增加,可以使用
来创建make
var m1 map[int]string = make(map[int]string) m2 := make(map[int]string) //简要写法 m1[1] = "ok" //插入 (1 : ok)的key-value m2[1] = "good"
创建还可以指定容量,make
,make(map[key类型]value类型,cap)
为容量,可以省略。超出容量会自动扩容,但为了性能还是尽量提供一个大概的初始值。cap
- 获取指定key的value
(获取key为1的value)a:=m[1]
- 插入一个k-v对(或对指定key进行修改)
m[1]="ok"
-
判断键值对是否存在
上面获取指定key的值
还有另外一种用法:map[key]
,即返回两个值,第二个值value,isPresent = map[key]
是布尔类型,如果该key存在,该值为isPresent
,且true
为该key对应的值;如果key不存在,则value
为isPresent
,且false
为空值。value
if value, ok := map1[key1];ok{ fmt.Println(value) }
- 删除指定key的键值对
delete(map1, key1)
- 使用
对map进行遍历for-range
如果指向获取key或者value,可以这么使用map1 := make(map[int]string) map1[1] = "a" map1[2] = "b" map1[3] = "c" for key, value := range map1 { fmt.Println(key,value) }
需要注意的是,for key := range map1 { fmt.Println(key) } for _, vlaue := range map1 { fmt.Println(value) }
中获得的key和value值都是副本,直接对这两个值进行修改并不会对原来的map有影响。需要用for
才能真正改变map中的值map[key]
- map类型的切片:注意需要使用两次
,第一次分配切片,第二次分配切片中每个map元素make
sm := make([]map[int]string, 5) for i := range sm { sm[i] = make(map[int]string,1) sm[i][1]="ok" }
- 嵌套map的使用:即map中value也是map类型,这种情况同样需要注意每一个嵌套的map也需要进行
之后才能使用(初始化分配空间)make
var m2 map[int]map[int]string m2 = make(map[int]map[int]string) m2[1] = make(map[int]string) //value中嵌套的map也需要进行初始化 m2[1][1] = "ok1"