数组和切片
数组
数组是一个单一元素类型,长度固定的元素序列,其在内存中的位置是连续的,如下
var x [5]int
x[4] = 100
和strings
一样,数组的下标是从0
开始计数的, 由于数组的长度是固定的,对数组的元素进行删减操作将会比较繁琐,因此比较常用的是对数组进行更高层次的封装的slice
初始化
数组的创建需要指明数组的长度以及对应存储元素的类型,一个数组一旦被声明,那么其长度和元素类型将会是不可在变更的
如果你需要在数组中存储更多的元素,那么你需要创建一个新的长度的数组,并将旧数组的元素复制过去
声明一个数组并设置其元素为初始值
var array [5]int
使用字面量声明一个数组
array := [5]int{1,2,34,5,6}
利用推导声明一个数组,数组的容量取决于初始化的时候,给定元素的数量
array := [...]int{1,2,54,3,4}
声明并初始化特定元素的值
array := [5]int{1:10, 2:20}
指针数组
array := *int{0:new(int), 1:new(int)} *array[0] = 10 *array[1] = 20 fmt.Println(array) // [0xc420014118 0xc420014130 <nil> <nil> <nil>]
此时打印出来的将会是一个存放了元素地址的数组
在Go中,一个数组就是一个值,因此可以对其进行赋值操作,变量的名称代表整个数组,一个相同类型的数组可以进行相互赋值(相同的类型指的是:长度相同,元素类型相同):
var array1 [5]string
array2 := [5]string{"a", "b", "c", "d", "e"}
array1 = array2
fmt.Println(array1)
以上将两个数组进行赋值操作,赋值之后,不同数组内的相同位置的元素代表的是不同地址中的值,但是对于指针数组来说,进行赋值操作之后,两个数组内的相同位置的元素指向的是相同位置中的值
var array1 [3]*string
array2 := [3]*string{new(string), new(string), new(string)}
*array2[0] = "a"
*array2[1] = "b"
*array2[2] = "c"
array1 = array2
fmt.Println(array1) //[0xc42007c1b0 0xc42007c1c0 0xc42007c1d0]
fmt.Println(array2) //[0xc42007c1b0 0xc42007c1c0 0xc42007c1d0]
多维数组
声明
var array [4][2]int
//使用字面量声明并初始化多维数组
array := [4][2]int{{10,12}, {20,21}, {30,31}, {40, 41}}
//声明并初始化特殊位置的值的数组
array := [4][2]int{1:{20,21}, 3:{40,41}}
array1 := [4][2]int{1:{0:20}, 3:{1:41}}
与一维数组一样,也可以通过下标来获取元素,相同类型的元素可以进行相互赋值
数组作为值进行传递
将数组作为值在函数中进行传递,是一个非常耗费内存对性能影响非常大的一个操作,当使用变量在函数中进行传递的时候,是将整个变量的值进行拷贝,然后再进行传递,比如:
var array [1e6]int
foo(array)
func foo(array [1e6]int){
//TODO
}
每一次foo
函数进行调用的时候,8M的内存将会被占用,当传递的值是一个数组的时候,所有的数据将会被复制一份,因此我们需要使用指针来解决这个问题
var array [1e6]int
foo(&array)
fun foo(array *[1e6]int){
//TODO
}
切片
切片是对数组的分割,与数组的共同点是可被索引以及含有长度,不同于数组的是,其长度是可变的
var x []float64
切片的声明和数组的声明唯一的不同就是,切片的声明在括号中没有长度的限制,如上就声明了一个长度为0的切片,这个时候是不能对切片进行任何的操作
如果需要创建一个切片,应该使用内置的make
函数:
x := make([]float, 5)
如上创建了一个与底层数组(长度为5)相关联的切片,切片总是和数组进行相关联,因此切片的长度不能比其底层的数组长,只能小于或等于其底层数组长度
当然,make
函数还接受第三个参数
x := make([]float64, 5, 10)
其中,10代表的是其底层数组的容量,因此这里引出了切片的两个重要的属性:长度和容量。
- 长度,切片所包含的元素的数量,可以使用
len(s)
来进行查看 - 容量,切片的第一个元素对应的底层数组位置的开始,到数组末尾的元素的元素数量,使用
cap(s)
进行查看
另外一种创建切片的方法是使用[low:high]
表达式:
arr := [5]float64{1,2,3,4,5}
x := arr[0:5]
需要注意的是,该表达式是一个左开右闭的,只会包含左边的区间,更为便利的是我们可以省略low
或者high
,或者两者都进行省略
对于索引操作,Go内置了两个与切片相关的操作函数append
和copy
函数
append
该函数添加元素到切片的末尾
- 如果切片的容量足够,那么元素将会被直接添加到切片末尾,切片长度改变,容量不变
- 如果切片容量不足,将会创建一个新的数组,切片中的所有元素将会被复制到新数组中,新添加的元素继续增加到新数组的末尾,并且返回一个新的切片
copy
copy(dst, src []Type)
将src
中的元素复制到dst
中,如果两个切片的长度不一样,那么两者之中较短的那一个将会被当做接受元素的目标
func main () {
slice1 := []int{1,2,3}
slice2 := make([]int, 2)
copy(slice2,slice1)
fmt.Println(slice1, slice2)
}
如上所示,本意是将slice1
中的值复制到slice2
中,但是slice2
的容量为2,小于slice1
中的元素的长度,所以只有讲slice1
复制到slice2
中
切片的一个典型应用是表示一个子元素的一个列表,这样就可以通过下标快速检索一个元素