数组
- 创建方式:
以创建 int 类型数组为例
var array1 [3]int // 元素会被自动初始化为0值 => {0, 0, 0}
array2 := [3]int{1} // 位提供初始化的元素被初始化为零值 => {1, 0, 0}
arary3 := [3]int{1, 2: 3} // 指定索引2的元素初始化值为3 => {1, 0, 3}
array4 := [...]int{1, 2, 3} // 不直接指定长度,编译器会根据元素个数给定长度 => {1, 2, 3}
array5 := [...]int{1, 2: 3} // 指定索引 2 的元素初始化值为3,会影响该数组长度 => {1, 0, 3}
- 复合型数组:
复合型数组可以省略类型化标签
type Student struct {
name string
age int
}
studentArray := [...]Student {
{"studentA", 18}, // 省略结构体标签
{"studentB", 20}
}
多维数组
注意: 1. 多维数组仅允许第一维使用 ...
2. 内置函数 len 和 cap 都只返回第一维数组的长度比较
如果元素支持 == 比较,那么该数组也支持此操作
切片
切片本身是个只读对象,其工作机制累死数组指针的一种包装。可以基于数组或数组指针创建切片,以开始和结束索引位置确定引用的数组片段。不支持反向索引,世纪范围是一个右半开区间。属性 cap 表示切片所引用数组片段的真是长度,len 用于限定可读的写元素数量。另外,数组必须 addressable,否则会引发错误。
与数组区别:
- 切片创建不需要提前声明长度;而数组需要声明,操作数组索引大于该数组长度,会产生越界的异常。
- 切片可以通过 make([]int, 3, 5) 方式创建,第一个参数为类型,第二个参数为长度(len),第三个参数为容量(cap)
- 切片的长度(len)和 容量(cap)两个属性不一定相等;而数组的两个属性一定相等。切片底层是数组的一部分或者全部, cap 值为从切片引用该底层数组开头部分到该底层数组结尾的长度,而 len 值为该切片引用的底层数组部分的长度。
- 切片是引用类型,而数组是值类型。说白了就是切片传递的是引用,只要对任意一个地方修改原切片值也会改变;而数组是值类型,每次传递相当于重新分配内存创建了一个新的数组,各个之间相互独立不影响。
- 切片不支持比较操作,即使元素类型支持也不行,仅可以判断您是否是 nil
- 创建方式:
sliceA := make([]int, 2, 5) // len = 2 cap = 5 => {0, 0}
sliceB := make([]int2, 2]) // len = 2 cap = 5 => {0, 0}
sliceC := []int{1, 2, 3, 5: 30} // len = 6 cap = 6 => {1, 2, 3, 0, 0, 30}
注意:
下面两种声明方式,前者只是定义了一个 []int 类型变量,并未执行初始化操作;而后者则用初始化表达式完成了全部创建过程。
var sliceA []int // sliceA == nil true
sliceB := []int{} // sliceB == nil false
- 优势
- 很显然,切片只是很小的结构体对象,用来代替数组传参可以避免复制开销
- make 函数允许在运行期动态制定数组长度,绕开了数组类型必须使用编译期常量的限制。
然而,并非所有时候都适合使用切片代替数组,因为切片底层数组可能会在堆上分配内存, 而且小数组在栈上拷贝的消耗也未必就比 make 代价大。
- append
向切片尾部添加数据(slice[len]),返回新的切片对象(如果超过 cap 限制,则为心切片独享重新分配内存和数组,所以要返回新的切片对象)。向 nil 切片追加数据时,会为其分配底层数组内存。
slice := make([]int, 0, 5) // {}
sliceA := append(slice, 10) // {10,}
sliceB := append(sliceB, 20, 30) // {10, 20, 30}
sliceC := append(sliceB, 100, 200, 300) // {10, 20, 30, 100, 200, 300} => 超出 cap, 新切片的地址已经变了
注意:
- 是超出切片 cap 限制,而非超出底层数组长度限制,因为 cap 可小于数组长度。
- 新分配数组长度是原 cap 的 2 倍,而非原数组的 2 倍(并非总是 2 倍,对于较大的切片,会尝试扩容 1/4,以节约内存)
- 因为存在重新分配底层数组的缘故,建议预留足够空间,避免多次分配复制的内存开销
- copy
在两个切片对象间复制数据,允许指向同一底层数组,允许目标区间重叠。最终所复制长度以较短的切片长度(len)为准。
slice := []int{1, 2, 3, 4, 5}
sliceA := slice[:] // {1, 2, 3, 4, 5}
sliceB := slice[1:3] // {2, 3}
n:= copy(sliceA, sliceB) //sliceA:{2 3 3 4 5} sliceB:{3, 3} n: 2
注意
- 如果切片长时间引用大数组中很小的片段,建议新建独立切片,复制出所需数据,以便于原数组内存被及时回收。