Golang 入门资料+笔记

Go之Slice(切片)

2019-07-12  本文已影响10人  Jabir_Zhang

内部结构

切片表面上用起来像是一个可变数组,但它其实是一个结构体,内部结构如下:


切⽚内部结构

切片分为三个参数

  1. 指针,指向一片连续的存储空间,也就是数组
  2. len(长度),我们可以访问的数组元素个数
  3. cap(容量),指针指向的数组的储存空间的长度

len和cap到底具体是什么意思,有什么区别?
我们可以通过代码来打印下长度和容量来观察

var s0 []int
t.Log(len(s0), cap(s0)) //打印结果0 0
s0 = append(s0, 1)
t.Log(len(s0), cap(s0)) //打印结果1 1

一开始初始化,没有元素,所以len和cap都是0。append一个元素1后,len和cap都增长成了1。

s1 := []int{1, 2, 3, 4}
t.Log(len(s1), cap(s1)) //打印结果4 4

len和cap都是4。

s2 := make([]int, 3, 5)
t.Log(len(s2), cap(s2))  //打印结果3 5
t.Log(s2[0], s2[1], s2[2]) 
t.Log(s2[0], s2[1], s2[2], s2[3]) //报错,len仅为3,因此访问第4个元素报错
s2 = append(s2, 1)
t.Log(s2[0], s2[1], s2[2], s2[3])
t.Log(len(s2), cap(s2)) //打印结果4 5

在申明切片的时候用make,指定len3和cap5,因此打印出来len为3、cap为5。此时注意我输出了切片各个元素,输出3个元素的时候正产输出,但第4个的时候报错了,可见len代表的就是我们能访问的数组个数。append后,len增加了1,但cap因为len并未大于cap,因此cap仍为5。

切片如何实现可变长

先看一段代码和输出就明白了

s := []int{}
for i := 0; i < 10; i++ {
   s = append(s, i)
   t.Log(len(s), cap(s))
}
打印结果
可以发现随着长度len的增长,容量cap也在增长。随着len要超过cap的时候cap会翻倍增长。

PS:看到这里也理解为什么切片增加元素的代码是s = append(s, i)这样写。按照感觉应该append(s, i)就可以,为什么前面还需要重新赋值。因为我们也看到了随着cap的增长,他的存储空间地址发生了变化,并不是在原有存储空间里增加元素。

值得注意的一点是,slice切片的内存自增长的代价,涉及到性能调优。

切⽚共享存储结构

切⽚共享存储结构
如何看待此图呢,所谓共享存储,比如图中Q2和summer切片,因为共享months切片的存储空间,修改任一方的元素,都会对另外一方造成影响。

还有一点需要注意:Q2截取了months切片中的下标4到7的元素,长度len为3,但cap是9;summer截取了months切片中的下标6到9的元素,长度len为3,但cap是7。

year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep","Oct", "Nov", "Dec"}
Q2 := year[3:6]
t.Log(Q2, len(Q2), cap(Q2))
summer := year[5:8]
t.Log(summer, len(summer), cap(summer))
summer[0] = "Unknow"
t.Log(Q2)
t.Log(year)
打印结果
看打印结果可以发现,修改了summer中的一个元素后,无论是Q2还是months中的元素都发生了改变。
上一篇 下一篇

猜你喜欢

热点阅读