2.1 数组
数组初始化方式常用的有3种,至于其它的用的很少,就不用管了,常用方式如下:
var a[4]intb := [4]int{2, 4}c := [...]int{2, 4}
Go数组是值类型,赋值和传参会复制整个数组数据,为了避免数据复制,可以使用数组指针:
func test(x *[2]int) { x[1] += 1}func main() { a := [2]int{2, 3} test(&a)}
最后需要区分指针数组和数组指针的区别:
func main() { x,y := 1, 2 a := [...]*int{&x, &y} // 元素为指针的指针数组 p := &a // 存储数组地址的指针}
Go的数组,其实我们用的不多,一般大家都用切片,所以对于数组,掌握上述知识就可以了,其它关于数组的知识,需要用的时候,查阅相关资料即可。
2.2 切片
2.2.1 创建切片切片属于引用类型,初始化方式主要有2种,分别为:
s1 := make([]int, 4, 6)s2 := []int{10,20,30,40,50,60}
s1 := make([]int, 4, 6)的示意图如下,由于 len = 4,所以后面2个暂时访问不到,但是容量还是在,数组里面每个变量都是0 。
s2 := [] int{10,20,30,40,50,60}的示意图如下:
切片的操作符s[i:j:k],j和k是个开区间,详见下图:
2.2.2 nil和空切片
nil切片的指针指向nil,表示一个不存在的切片:
var slice []int
空切片一般会用来表示一个空的集合。比如数据库查询,一条结果也没有查到,那么就可以返回一个空切片。
silce := make([]int , 0) slice := []int{}
需要说明的一点:不管是使用 nil 切片还是空切片,对其调用内置函数 append,len 和 cap 的效果都是一样的。然后切片只能和nil判等,不支持切片判等。
2.2.3 切片扩容
我们先看一幅图:
Go 切片扩容策略:如果切片的容量小1024个元素,于是扩容的时候就翻倍增加容量。上面那个例子也验证了这一情况,总容量从原来的4个翻倍到现在的8个。一旦元素个数超过1024个元素,那么增长因子就变成 1.25,即每次增加原来容量的1/4。下面我们看一种情况,当扩容时没有新建一个新的数组的情况,这里容易出问题:
func main() { array := [4]int{10, 20, 30, 40} slice := array[0:2] // 10 20 newSlice := append(slice, 50) // 10 20 50 newSlice[1] += 10 // 10 30 50// 这里slice=[10 30],array=[10 30 50 40],入坑!!!}
内部情况见图,可以看出slice、newSlice和array底层共用一个数组,当修改newSlice[1]时,因为底层数据被修改,其它也都被修改了,这样非常容易产生莫名的Bug!
2.2.4 切片遍历
如果用 range 的方式去遍历一个切片,拿到的 Value 其实是切片里面的值拷贝,每次打印 Value 的地址都不变,所以仅修改Value的值,是不会改变Slice中的数据,这点切记!!!