数组和切片的区别

slice 的底层数据是数组,slice 是对数组的封装,它描述一个数组的片段。两者都可以通过下标来访问单个元素。

两者不一样

举例说明

go和js语言表达不太一样,很少说数组这一个概念吗,更多时候对数组的表达会说切片,因为数据长度是其类型的一部分,比如[3]int和[4]int就是不同类型。

但是切片就很灵活,可以动态扩容(这个是数组做不到的),其根本原因是结构定义不一样

1
2
3
4
5
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}

看个例子,例子来自雨痕大佬《Go学习笔记》第四版,P43页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := slice[2:5]
s2 := s1[2:6:7]

s2 = append(s2, 100)
s2 = append(s2, 200)

s1[2] = 20

fmt.Println(s1)
fmt.Println(s2)
fmt.Println(slice)
}

s1slice 索引2(闭区间)到索引5(开区间,元素真正取到索引4),长度为3,容量默认到数组结尾,为8。 s2s1 的索引2(闭区间)到索引6(开区间,元素真正取到索引5),容量到索引7(开区间,真正到索引6),为5。

image-20240721173626352

接着,向 s2 尾部追加一个元素 100:

s2 容量刚好够,直接追加。不过,这会修改原始数组对应位置的元素。这一改动,数组和 s1 都可以看得到。

image-20240721173645297

再次向 s2 追加元素200:

这时,s2 的容量不够用,该扩容了。于是,s2 另起炉灶,将原来的元素复制新的位置,扩大自己的容量。并且为了应对未来可能的 append 带来的再一次扩容,s2 会在此次扩容的时候多留一些 buffer,将新的容量将扩大为原始容量的2倍,也就是10了。

image-20240721173656520

最后,修改 s1 索引为2位置的元素:

这次只会影响原始数组相应位置的元素。它影响不到 s2 了,人家已经远走高飞了。

s1[2]=20

再提一点,打印 s1 的时候,只会打印出 s1 长度以内的元素。所以,只会打印出3个元素,虽然它的底层数组不止3个元素。