什么是array
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的入参传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。
因为长度只能在初始化的时候确定,所以其实array在go中运用的并不是很多,而slice就比较方便了。
array的内存分配
直接就是一段连续的内存空间。
如何使用array
比较常规的一种写法,当然go提供了很丰富的数组初始化的方法。
1 2 3 4 5 |
var a [10]int for i:=0;i<len(a);i++{ a[i]=i } fmt.Printf("a:%v\n",a) |
使用对一个数组进行切片来形成新的数组
1 2 3 4 5 6 7 8 9 10 11 |
var a [10]int for i:=0;i<len(a);i++{ a[i]=i } b:=a[5:len(a)]; fmt.Printf("a:%v\n",a) fmt.Printf("b:%v\n",b) /** output: a:[0 1 2 3 4 5 6 7 8 9] b:[5 6 7 8 9] */ |
使用array时需要注意的点
在使用切片来初始化数组的时候实际上是传递的引用。从下面的例子可以很简单的得出传递引用的结论。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var a [10]int for i:=0;i<len(a);i++{ a[i]=i } b:=a[5:len(a)]; fmt.Printf("a:%v\n",a) fmt.Printf("b:%v\n",b) for i:=0;i<len(b);i++{ b[i]=b[i]+1 } fmt.Printf("a:%v\n",a) fmt.Printf("b:%v\n",b) //a=a[:11]; /** output a:[0 1 2 3 4 5 6 7 8 9] b:[5 6 7 8 9] a:[0 1 2 3 4 6 7 8 9 10] b:[6 7 8 9 10] */ |
什么是slice
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
slice的内存分配
在slice当中有两个”长度”,一个叫做len也就是长度,一个叫做cap也就是容量。
len是自己现在能用的容量,cap是已经分配的内存容量,可以通过a=a[:cap(a)];
这种方法来扩充len。对超过len的数组元素进行操作会发生数组越界错误。
slice的存储实际上就是一个指向array的指针,一个len,一个cap。
slice的使用(slice的传递与容量扩充)
slice的创建
1 2 3 4 5 6 7 8 9 10 11 |
var identifier []type var slice1 []type = make([]type, len) slice1 := make([]type, len) make([]T, length, capacity) s :=[] int {1,2,3 } s := arr[:] //这种情况是传递引用 |
有丰富的创建方法,需要注意的是,会存在空slice的情况,可以使用与nil
进行比较来判断是否为空,也可以判断len与cap是否同时为零来判断是否为空slice。
make方法可以创建slice,第一个参数为类型,第二个参数为长度,第三个参数为容量。
slice的扩容
1.使用切片方法进行扩容(当len小于cap时)
1 |
s := s[:cap(s)] |
2.使用append的方法进行扩容
这个扩容方法是可以扩大cap的,但是会有一些问题。
1 |
func append(s []T, vs ...T) []T |
1 2 3 4 5 6 7 8 |
a:=make([]int,10,11) a=a[:11]; a[0]=10 b:=append(a,100) b[0]=20 c:=append(b,100) c[0]=30 fmt.Println(a[0],b[0],c[0]) |
大家来猜猜这一段的输出是多少呢?
10 30 30
这是最终的输出结果,会不会觉得有点奇怪呢?
append方法在调用的时候,若扩容的是不增加cap的情况,那么就是按引用传递,如果是增加了cap那么就是创建了一个新的slice并返回。
所以在使用append方法进行扩容时,请注意是否扩充了cap,在追加元素扩充的时候默认是cap进行翻倍。