前言
提到数组可能大家知道,但是切片又是什么东西呢?切片对应的英语单词是slice
,如果有了解过Python
的朋友可能会对切片有种焕然大悟的感觉。而如果要在Java
中找到个类似的数据结构,那就是ArrayList
。
简单来说切片slice
可以看做是一个动态长度的数组,它的底层是一个数组,不过它可以通过重建一个数组达到扩容的目的。
下面会分别介绍数组和切片
数组
go
中的数组是很常见的定长数组,数组的长度在声明时需要确定。
声明数组
数组声明的语法很简单,使用var
声明即可
var a [3] int // 数组声明方式 var 数组名 [数组长度] 数据类型
数组中的元素未初始化时,则为对应类型的默认值。当然也可以使用:=
来声明数组
a := [3] int {} // 数组声明方式 数组名 := [数组长度] 数据类型 {}
需要注意数组声明时,需要在声明时指定数组的长度。也可以通过初始化数组中每个元素来指定数组长度
a := [] int {1,2,3}
除了一维数组以外,也支持多维数组,下面将演示二维数组声明
a := [3] [3] int {}
访问数组
数组可以通过下标索引访问数组中特定位置的元素,需要注意索引是从0
开始的。
package main
import "fmt"
func main() {
a := [] int {1,2,3}
fmt.Println(a[0])
}
上面例子的运行结果为1
,当然也可以使用for
(后续会详细介绍)遍历数组元素
package main
import "fmt"
func main() {
a := [] int {1,2,3}
for i:=0;i<len(a);i++{
fmt.Println(a[i])
}
}
上面例子的中使用len(a)
获取数组长度,运行结果如下
1
2
3
切片
切片是对数组部分元素(也可是全部元素)的引用,为什么需要切片呢?因为数组是值类型,赋值和传参会复制整个数组。而使用切片则不需要复制整个数组。
切片声明
切片既可以在一个数组基础上声明,也可以直接声明(会自动创建引用的数组)
下面会演示怎么基于数组声明切片
arr := [] int {1,2,3,4,5} // 声明数组
slice := arr[0:3:5] //切片声明 切片名 := 数组名[最左索引left:最右索引right:切片最大长度max]
需要注意的是最左索引left
、最右索引right
和切片最大长度max
都可以省略,例如这样
slice := arr[:] // 等价于引用整个原数组
直接声明切片时需要用到展开符号...
slice := [...] int {1,2,3}
也可以使用make
创建一个元素未初始化的切面
slice := make([]int, 5) // 声明了一个长度为6的切片
切片使用
前文提到切片可以作为一个动态长度的数组使用(切片遍历方式与数组一致),那么切片是在什么时候会触发引用的数组扩容(替换新的数组)呢?
这个就需要提到slice
中的一个内置属性cap
,这个属性就是扩容阈值,切片长度大于这个阈值时,自动重新分配新底层数组,下面将会用一个例子演示切片如何动态扩容
package main
import "fmt"
func main() {
slice := make([]int, 5) // 声明一个长度和扩容阈值为5的切片
fmt.Println("length:",len(slice)) // 输出切片长度
fmt.Println("cap:",cap(slice)) // 输出切片的扩容阈值
slice =append(slice,6) // 切片增加一个元素6
fmt.Println("newLength:",len(slice)) // 输出切片新长度
fmt.Println("newCap:",cap(slice)) // 输出切片新的扩容阈值
}
运行结果
length: 5
cap: 5
newLength: 6
newCap: 10
从运行结果可以得知,切片的阈值增长策略为当新长度大于阈值时,新阈值为原阈值的2
倍