基本概念
切片看上去是一个可变长度的数组,实际上底层是一个结构体。结构体包含三个基本元素:1,指针(*[]type),指向一个连续的内存空间,也就是一个数组; 2,元素个数(len),可以访问的元素的个数;3,指针指向的数组的空间的长度(cap)
其中,len个元素会被初始化为默认零值,未初始化的元素不可访问。切片结构体详解如下图:
代码
func TestSliceInit(t *testing.T) { // var s0 []int //如果声明时不赋值,此时切片中元素个数和指针指向的空间长度固定均为0,其实此时切片s并不存在 s1 := []int{1, 2, 3, 4} //和数组声明和像,但是[]没有指定长度。 t.Log(&s1[0], len(s1), cap(s1)) //s1 = append(s1, 5) //使用append可以进行数据追加。为什么append还需要进行赋值? }
结果:
切片如何实现可变长
func TestSliceGrowing(t *testing.T) { s := []int{} //t.Log(&s[0]) //没有赋值前,其实s并不存在 t.Log(len(s), cap(s)) for i := 0; i < 10; i++ { s = append(s, i) t.Log(&s[0], len(s), cap(s)) } }
结果:
cap增长规律:当len长度小于等于cap长度,cap不增长;当len长度等于cap长度,且有新元素加入,此时cap长度正常,增幅为当前cap长度的2倍。
-
当cap不增长时,新增元素,其实就是往数组里面追加数据
-
当cap增长时,新增元素,其实创建了一个新的存储空间,把旧的存储空间数据拷贝到新空间。这里就要考虑到切片自增长的代价。这也就是为什么append()需要赋值的原因
切片共享存储结构
slice是一个结构体,包含一个指针,指向了后端连续存储空间,此时如果是否可以做共享存储?多个slice,指向同一个存储空间。
如下场景:切片 year 表示一年十二个月,切片Q2从year中截断取[3,6)元素;切片summer从year中截断取[5,8)元素。此时三个切片其实共享了后端的存储,任何一个切片进行数据变更,每个共享数据的切片指向的该值都会发生变化。
func TestSliceShareMemory(t *testing.T) { year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} Q2 := year[3:6] t.Log(Q2, &Q2[0], len(Q2), cap(Q2)) summer := year[5:8] t.Log(summer, &summer[0], len(summer), cap(summer)) t.Log(&Q2[2]) summer[0] = "Unknow" //修改切片summer[0]数据的值 t.Log(Q2) t.Log(summer) t.Log(year) }
结果:
存储结构:
数组和切片比较
-
容量是否可以伸缩
-
数组不可以伸缩,切片可以
-
-
是否可以进行比较
-
数组比较需要是相同维数,相同长度;切片看似是数组,但是不能进行比较
-
代码:(切片不能比较)
func TestSliceComparing(t *testing.T) { a := []int{1, 2, 3, 4} b := []int{1, 2, 3, 4} if a == b { //invalid operation: a == b (slice can only be compared to nil) t.Log("equal") } }
结果://invalid operation: a == b (slice can only be compared to nil)