Go系列教程2 - 数组和切片

590 阅读5分钟

往期精选(欢迎转发~~)

数组

数组初始化方式常用的有3种,至于其它的用的很少,就不用管了,常用方式如下:

var a[4]int
b := [4]int{24}
c := [...]int{24}

Go数组是值类型,赋值和传参会复制整个数组数据,为了避免数据复制,可以使用数组指针:

func test(x *[2]int) {
    x[1] += 1
}
func main() {
    a := [2]int{23}
    test(&a)
}

最后需要区分指针数组和数组指针的区别:

func main() {
    x,y := 12
    a := [...]*int{&x, &y} // 元素为指针的指针数组
    p := &a // 存储数组地址的指针
}

Go的数组,其实我们用的不多,一般大家都用切片,所以对于数组,掌握上述知识就可以了,其它关于数组的知识,需要用的时候,查阅相关资料即可。

切片

创建切片

切片属于引用类型,初始化方式主要有2种,分别为:

s1 := make([]int46)
s2 := []int{0,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是个开区间,详见下图:

nil和空切片

nil切片的指针指向nil,表示一个不存在的切片:

var slice []int

空切片一般会用来表示一个空的集合。比如数据库查询,一条结果也没有查到,那么就可以返回一个空切片。

silce := make([]int , 0)  
slice := []int{}

需要说明的一点:不管是使用 nil 切片还是空切片,对其调用内置函数 append,len 和 cap 的效果都是一样的。然后切片只能和nil判等,不支持切片判等。

切片扩容

我们先看一幅图:

Go 切片扩容策略:如果切片的容量小1024个元素,于是扩容的时候就翻倍增加容量。上面那个例子也验证了这一情况,总容量从原来的4个翻倍到现在的8个。一旦元素个数超过1024个元素,那么增长因子就变成 1.25,即每次增加原来容量的1/4。

下面我们看一种情况,当扩容时没有新建一个新的数组的情况,这里容易出问题:

func main() {  
   array := [4]int{10203040}
   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!

切片遍历

如果用 range 的方式去遍历一个切片,拿到的 Value 其实是切片里面的值拷贝,每次打印 Value 的地址都不变,所以仅修改Value的值,是不会改变Slice中的数据,这点切记!!!

欢迎大家多多点赞,更多文章,请关注微信公众号“楼仔进阶之路”,点关注,不迷路~~