1.1.1. 一、切片的介绍。
1、声明切片的语法:
var 切片名 []类型
例如:var a [] int
表示声明一个切片[]int类型的变量a。
2、切片的长度是可以变化的,因此切片是一个可以动态变化的数组。
3、切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
4、切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样。
示例如下:
func main() {
intArr := [...]int{11,21,30,50}
/**
1、声明一个切片。名字是slice1
2、intArr[1:3] 表示slice1 引用intArr数组下标从1到3的元素。包含1不包含3。
*/
slice1 := intArr[1:3]
//intArr= [11 21 30 50]
fmt.Println("intArr=", intArr)
//slice1的元素= [21 30]
fmt.Println("slice1的元素=",slice1)
//slice1的元素个数= 2
fmt.Println("slice1的元素个数=",len(slice1))
//slice1的容量= 3
fmt.Println("slice1的容量=", cap(slice1)) //切片的容量可以动态变化
}
示例2如下:数组的intArr[1]地址等于slice1[0]的地址。表示创建切片的时候只是把数组 intArr 元素的地址复制给了切片slice1。改变intArr[1]、slice1[0]其中一个值,另一个值会一起变化。
func main() {
intArr := [...]int{11,21,30,50}
slice1 := intArr[1:3]
// intArr[1]的地址= 0xc0000600c8
fmt.Println("intArr[1]的地址=", &intArr[1])
// slice1[0]的地址= 0xc0000600c8
fmt.Println("slice1[0]的地址=",&slice1[0])
}
5、切片的底层结构。
5.1、slice 是一个引用类型。
5.2、slice 从底层来说,其实就是一个数据结构(struct 结构体)。其中指针指向起始位置;len表示元素的个数;cap表示切片的容量。
type slice struct {
ptr *[2]int
len int
cap int
}
1.1.2. 二、创建切片的3种方式。
方式1:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的。
intArr := [...]int{11,21,30,50}
slice1 := intArr[1:3]
方式2:通过 make 来创建切片。
基本语法:var 切片名 []type = make([]type, len, [cap])
参数说明:type:就是数据类型; len:大小; cap :指定切片容量(可选), 如果你分配了 cap,则要求 cap>=len。
var slice2 []float64 = make([]float64, 3, 5)
slice2[0] = 20 //给切片的元素赋值
// slice2= [20 0 0]
fmt.Println("slice2=", slice2)
- 通过 make 方式创建切片可以指定切片的大小和容量 。虽然容量为5,但是只初始化3个元素。
- 如果没有给切片的各个元素赋值,那么就会使用元素的数据类型的零值。
- 通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去访问各个元素。
方式3:定义一个切片,直接就指定具体数据。
var strSlice = []string{"tom","jack"}
//strSlice= [tom jack]
fmt.Println("strSlice=", strSlice)
//strSlice 长度= 2
fmt.Println("strSlice 长度=", len(strSlice))
// strSlice 容量= 2
fmt.Println("strSlice 容量=", cap(strSlice))
1.1.3. 三、切片的遍历(和数组一样)。
切片的遍历和数组一样,也有两种方式 :
- 1、 for 循环常规方式遍历 。
- 2、for-range 方式遍历。
var strSlice = []string{"tom","jack"}
//普通遍历
for i := 0; i < len(strSlice); i++ {
fmt.Printf("strSlice[%v]=%v \n",i,strSlice[i])
}
// for-range遍历
for i,v:= range strSlice {
fmt.Printf("strSlice[%v]=%v \n",i,v)
}
1.1.4. 四、 切片的使用的注意事项。
1、切片初始化时var slice = arr[startIndex:endIndex]
说明:从 arr 数组下标为 startIndex,取到 下标为 endIndex 的元素(不含 arr[endIndex])。
2、切片初始化时,仍然不能越界。范围在 [0-len(arr)] 之间,但是可以动态增长。
var slice = arr[0:end] 可以简写 var slice = arr[:end]
var slice = arr[start:len(arr)] 可以简写: var slice = arr[start:]
var slice = arr[0:len(arr)] 可以简写: var slice = arr[:]
3、cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
4、切片可以继续切片。
例子如下:slice2是slice1继续切片之后的结果。arr、slice1、slice2指向的元素地址相同。改变其中一个另一个会改变。
var arr = [...]int{11,2,3,54}
var slice1 = arr[1:4] // 切片的元素是arr下标从1到4的元素
fmt.Println(slice1) // [2 3 54]
//切片继续切片
var slice2 = slice1[1:] // 切片slice2的元素是slice1下标从1开始之后的元素
fmt.Println(slice2) // [3 54]
5、 用 append 内置函数,可以对切片进行动态追加。
示例如下:使用append给slice1 增加了3个元素。原来的slice1 不变,返回的slice2是追加元素之后的数组。
var slice1 = []int{6,13}
slice2 := append(slice1, 4, 8, 90)
fmt.Println(slice1) // [6 13]
fmt.Println(slice2) // [6 13 4 8 90]
// slice1... 表示把slice1的元素遍历放在这里做参数。
//把slice1的元素追加给slice1。
slice3 := append(slice1, slice1...)
fmt.Println(slice3) // [6 13 6 13]
append函数的底层原理分析:
切片 append 操作的本质就是对数组扩容
go 底层会创建一下新的数组 newArr(安装扩容后大小)
将 slice1 原来包含的元素拷贝到新的数组 newArr,并且返回新的数组。
可以把切片的元素追加给另一个切片:slice3 := append(slice1, slice1...) 。第二个参数slice1... 必须是切片,不能是数组。
6、切片的拷贝操作。
切片使用 copy 内置函数完成拷贝,例子如下:
var slice1 = []int{6,13}
//slice2 := make([]int,3)
slice2 := []int{1,5,9}
//把 slice1 的元素拷贝给slice2。
// slice1 的元素会按照下标覆盖 slice2的元素
copy(slice2,slice1)
fmt.Println("slice2=",slice2) //slice2= [6 13 9]
fmt.Println("slice1=",slice1) //slice1= [6 13]
说明:
- copy(slice2,slice1) 函数的2个参数都必须是切片。
- copy(slice2,slice1) slice1 的元素会按照下标覆盖 slice2的元素。
- copy(slice2,slice1) 拷贝完成后,slice1和slice2的数据空间相互独立。改变slice2的元素值不影响slice1的元素值。同理改变slice1也不影响slice2。
拷贝数组的时候。如果接收方的数组长度不够。那只拷贝一部分。
例子如下:slice2只有一个元素,slice1有2个元素。把slice1拷贝给slice2。slice2只接受一个元素。
var slice1 = []int{6,13}
slice2 := make([]int,1)
copy(slice2,slice1)
fmt.Println("slice2=",slice2) //slice2= [6]
fmt.Println("slice1=",slice1) //slice1= [6 13]
7、切片是引用类型,所以在函数传参、变量赋值时,遵守引用传递机制。
8、切片元素的删除。
Go 语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素。示例代码如下:
seq := []string{"a", "b", "c", "d", "e"}
// 指定要删除元素的位置,就是"c"
index := 2
// 查看删除位置之前的元素和之后的元素
fmt.Println(seq[:index], seq[index+1:])// [a b] [d e]
// 将删除点前后的元素连接起来
seq = append(seq[:index], seq[index+1:]...)
fmt.Println(seq) // [a b d e]