切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装。
切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个与指向数组的动态窗口。
给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是,切片的长度可以在运行时修改,最小为 0 最大为相关数组的长度:切片是一个长度可变的数组。
切片定义的基本语法:
var 切片名[]类型
如:var a[]int
package main
import(
"fmt"
)
func main(){
var arr [5]int = [...]int{
5,4,3,2,1}
//定义一个切片
//引用arr数组的起始下标为1,最后的下标为3(但不包括3)
slice := arr[1:3]
fmt.Println("arr=",arr)
fmt.Println("slice 的元素是 =",slice)
fmt.Println("slice 的元素个数 =",len(slice))
fmt.Println("slice 的容量 =",cap(slice))//切片的容量是可以动态变化的
}
type slice struct{
ptr *[2] int
len int
cap int
}
nil切片和空切片也是常用的。
var slice []int
nil 切片被用在很多标准库和内置函数中,描述一个不存在的切片的时候,就需要用到 nil 切片。比如函数在发生异常的时候,返回的切片就是 nil 切片。nil 切片的指针指向 nil。
空切片一般会用来表示一个空的集合。比如数据库查询,一条结果也没有查到,那么就可以返回一个空切片。
slice := make([]int, 0)
slice := []int{
}
空切片和 nil 切片的区别在于,空切片指向的地址不是nil,指向的是一个内存地址,但是它没有分配任何内存空间,即底层元素包含0个元素。
最后需要说明的一点是。不管是使用 nil 切片还是空切片,对其调用内置函数 append,len 和 cap 的效果都是一样的。
定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例。
func main(){
var arr [5]int = [...]int{
5,4,3,2,1}
//定义一个切片
//引用arr数组的起始下标为1,最后的下标为3(但不包括3)
slice := arr[1:3]
fmt.Println("arr=",arr)
fmt.Println("slice 的元素是 =",slice)
fmt.Println("slice 的元素个数 =",len(slice))
fmt.Println("slice 的容量 =",cap(slice))//切片的容量是可以动态变化的
}
通过make来创建切片。
基本语法:
var 切片名 []type = make([]type,len,[cap])
参数说明:
案例演示
package main
import(
"fmt"
)
func main(){
var slice []int = make([]int ,5,10)
slice[1] = 10
slice[2] = 20
fmt.Println(slice)
fmt.Println("slice的size =",len(slice))
fmt.Println("slice的cap =",cap(slice))
}
定义一个切片,直接就指定具体数组,使用原理类似make的方式。
案例:
package main
import(
"fmt"
)
func main(){
var slice []string = []string{
"Casey","Lily","Sally"}
fmt.Println(slice)
fmt.Println("slice的size =",len(slice))
fmt.Println("slice的cap =",cap(slice))
}
切片的遍历和数组一样,也是有两种方式,for循环常规方法遍历,for-range结构遍历切片。
案例演示:
package main
import(
"fmt"
)
func main(){
var slice []string = []string{
"Casey","Lily","Sally"}
//常规for循环遍历切片
for i := 0; i < len(slice); i++{
fmt.Printf("slice[%d] = %v\n", i, slice[i])
}
//使用for-range结构遍历切片
for index, value := range slice{
fmt.Printf("i = %v, v = %v\n", index, value)
}
}
package main
import(
"fmt"
)
func main(){
var slice []string = []string{
"Casey","Lily","Sally"}
//通过append内置函数,可以对切片进行动态追加
slice = append(slice,"Jerry","Tom")
fmt.Println("slice =",slice)
//通过append将切片slice追加给slice
slice = append(slice,slice...)
fmt.Println("slice =",slice)
}
切片使用copy内置函数完成拷贝。
案例:
package main
import(
"fmt"
)
func main(){
var slice []string = []string{
"Casey","Lily","Sally"}
var slice1 = make([]string,5)
copy(slice1,slice)
fmt.Printf("slice = %v\n",slice)
fmt.Printf("slice1 = %v\n", slice1)
var slice2 []int = []int{
10,20,30}
var slice3 = make([]int,5)
copy(slice3,slice2)
fmt.Printf("slice2 = %v\n",slice2)
fmt.Printf("slice3 = %v\n", slice3)
}
var slice = arr[0:end] 可简写 var slice = arr[:end]
var slice = arr[start:len(arr)]可简写var slice = arr[start:]
var slice = arr[0:len(str)] 可简写 var slice = arr[:]
package main
import(
"fmt"
)
func main(){
var slice []string = []string{
"Casey","Lily","Sally"}
slice1 := slice[0:2]
//slice 和 slice1指向的数据空间是同一个,因此slice[1] = "abc"
slice1[1] = "abc"
fmt.Println("slice =",slice)
fmt.Println("slice1 =",slice1)
}