1.切片的概念

Go语言中数组的长度不可改变,但在很多应用场景中,在初始定义数组时,数组的长度并不可预知,这样的序列集合无法满足要求。Go中提供了另外一种内置类型切片(slice),弥补了数组的缺陷。从底层来看,切片引用了数组的对象。切片可以追加元素,在追加时可能使切片的容量增大。与数组相比,切片不需要设定长度,在[]中不用设定值,相对来说比较自由。

2.切片声明使用

2.1 通过make

//使用make()函数来创建切片
var slice1 = make([]int,3)
slice1[0] = 1
slice1[1] = 2
slice1[2] = 4
fmt.Printf("通过make关键字[slic1] 类型:%T 值:%v \n",slice1,slice1)
// 输出: 通过make关键字[slic1] 类型:[]int 值:[1 2 4]

2.2 直接初始化

// 直接初始化使用
slice2 := []int{1,2,4}
fmt.Printf("直接初始化使用[slic2] 类型:%T 值:%v \n",slice2,slice2)
// 输出: 直接初始化使用[slic2] 类型:[]int 值:[1 2 4]

2.3 通过数组截取

package main
import "fmt"
func main() {
// 定义一个数组
arr := [...]int{1,2,4,6,8,10,12,14,16}
// 从索引0开始截取到索引为4(不包括4)
slice3 := arr[0:4]
fmt.Printf("从索引0开始截取到索引为4(不包括4)[slice3] 类型:%T 值:%v \n",slice3,slice3)
// 索引从0开始时,0也可以省略不写
slice33 := arr[:4]
fmt.Printf("从索引0开始截取到索引为4(不包括4)[slice33] 类型:%T 值:%v \n",slice33,slice33)
// 从索引4开始截取到最后
slice4 := arr[5:]
fmt.Printf("从索引5开始截取到最后[slice4] 类型:%T 值:%v \n",slice4,slice4)
}
/**输出
从索引0开始截取到索引为4(不包括4)[slice3] 类型:[]int 值:[1 2 4 6]
从索引0开始截取到索引为4(不包括4)[slice33] 类型:[]int 值:[1 2 4 6]
从索引5开始截取到最后[slice4] 类型:[]int 值:[10 12 14 16]
*/

3.切片删除

3.1 删除第一个元素

package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1,2,3,4,5,6}
fmt.Printf("切片slice--> 值:%v len: %d cap: %d\n",slice,len(slice),cap(slice))
// 删除第一个元素
slice = slice[1:]
fmt.Printf("删除一个元素后--> 值:%v len: %d cap: %d\n",slice,len(slice),cap(slice))
}
/*输出
切片slice--> 值:[1 2 3 4 5 6] len: 6 cap: 6
删除一个元素后--> 值:[2 3 4 5 6] len: 5 cap: 5
*/

3.2 删除最后一个元素

package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1,2,3,4,5,6}
fmt.Printf("切片slice--> 值:%v len: %d cap: %d\n",slice,len(slice),cap(slice))
// 删除最后一个元素
slice = slice[:len(slice)-1]
fmt.Printf("删除一个元素后--> 值:%v len: %d cap: %d\n",slice,len(slice),cap(slice))
}
/*输出
切片slice--> 值:[1 2 3 4 5 6] len: 6 cap: 6
删除一个元素后--> 值:[1 2 3 4 5] len: 5 cap: 6
*/

3.3 删除指定位置元素

package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1,2,3,4}
fmt.Printf("切片slice--> 值:%v len: %d cap: %d\n",slice,len(slice),cap(slice))
index := 2
// 指定索引前面一部分
front := slice[:index]
// 指定索引后面一部分
behind := slice[index+1:]
// 拼起来
slice = append(front,behind...)
fmt.Printf("删除索引%d后--> 值:%v len: %d cap: %d\n",index,slice,len(slice),cap(slice))
}

4.len()和cap()

4.1 作用

切片的长度是切片中元素的数量。切片的容量是从创建切片的索引开始的底层数组中元素的数量。切片可以通过len()方法获取长度,可以通过cap()方法获取容量。数组计算cap()结果与len()相同

4.2 使用

package main
import "fmt"
func main() {
// 创建一个切片
slice := []int{1,2,4,6,8,10,12,14,16}
fmt.Printf("切片slice, len:%d cap:%d \n",len(slice),cap(slice))

// 清空切片slice赋值给slice1
slice1 := slice[0:0]
fmt.Printf("切片slice1, len:%d cap:%d \n",len(slice1),cap(slice1))

// 使用make指定容量创建切片
var slice2 = make([]int,4,8)
slice2[1] = 1
slice2[3] = 3
fmt.Printf("切片slice2, len:%d cap:%d \n",len(slice2),cap(slice2))

// 数组计算cap()结果与len()相同
arr := [...]int{1,2,4,6,8,10,12,14,16}
fmt.Printf("数组arr, len:%d cap:%d \n",len(arr),cap(arr))
}
/***输出:
切片slice, len:9 cap:9
切片slice1, len:0 cap:9
切片slice2, len:4 cap:8
数组arr, len:9 cap:9
*/

5.切片是引用类型

切片没有自己的任何数据。它只是底层数组的一个引用。对切片所做的任何修改都将反映在底层数组中。数组是值类型,而切片是引用类型。

5.1 函数传参

package main
import "fmt"
func testSlice(slice []int) {
// 修改切片的值
slice[0] = 100
}
func main() {
// 创建一个切片
var slice = make([]int,4,4)
slice = []int{1,2,3,4}
fmt.Printf("变量slice 类型: %T 内存地址: %p 值:%v \n",slice,&slice,slice)
// 调用函数修改
testSlice(slice)
fmt.Printf("调用函数后,变量slice 类型: %T 内存地址: %p 值:%v \n",slice,&slice,slice)
}
/**输出
变量slice 类型: []int 内存地址: 0xc00000c080 值:[1 2 3 4]
调用函数后,变量slice 类型: []int 内存地址: 0xc00000c080 值:[100 2 3 4]
*/

5.2 多切片共享

修改切片数值,当多个切片共享相同的底层数组时,对每个元素所做的更改将在数组中反映出来。

package main
import "fmt"
func testSlice(slice []int) {
// 修改切片的值
slice[0] = 100
}
func main() {
// 创建一个切片
var slice = make([]int, 4, 4)
slice = []int{1, 2, 3, 4}
fmt.Printf("变量slice 类型: %T 内存地址: %p 值:%v \n", slice, &slice, slice)
// 复制给另外一个切片
sliceCopy := slice
fmt.Printf("变量sliceCopy 类型: %T 内存地址: %p 值:%v \n", sliceCopy, &sliceCopy, sliceCopy)
// 从切片中截取
slice2 := slice[0:2]
fmt.Printf("变量slice2 类型: %T 内存地址: %p 值:%v \n", slice2, &slice2, slice2)
// 调用函数修改
testSlice(slice)
fmt.Printf("调用函数后---变量slice 类型: %T 内存地址: %p 值:%v \n", slice, &slice, slice)
fmt.Printf("调用函数后---变量sliceCopy 类型: %T 内存地址: %p 值:%v \n", sliceCopy, &sliceCopy, sliceCopy)
fmt.Printf("调用函数后---变量slice2 类型: %T 内存地址: %p 值:%v \n", slice2, &slice2, slice2)
}

6.append()和copy()

6.1 作用

  • append():用于往切片中追加新元素,可以向切片里面追加一个或者多个元素,也可以追加一个切片。
  • copy(): 不会建立源切片与目标切片之间的联系。也就是两个切片不存在联系,其中一个修改不影响另一个。

6.2 使用append()

package main
import "fmt"
func testSlice(slice []int) {
// 修改切片的值
slice[0] = 100
}
func main() {
// 定义一个切片类型,长度为0,容量为4
var slice = make([]int, 0, 4)

//向切片中添加1个元素 (不超过容量)
slice1 := append(slice,1)
fmt.Printf("变量slice1 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice1,len(slice1),cap(slice1),&slice1 )

// 向切片中添加多个元素(不超过容量)
slice2 := append(slice,3,4,5)
fmt.Printf("变量slice2 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice2,len(slice2),cap(slice2),&slice2 )

// 向切片中添加一个切片(超过容量)
newSlice := []int{1,2,3,4,5,6,7}
slice3 := append(slice,newSlice[:]...)
fmt.Printf("变量slice3 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice3,len(slice3),cap(slice3),&slice3 )

// 调用函数修改
testSlice(slice1)
fmt.Printf("调用函数后-变量slice1 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice1,len(slice1),cap(slice1),&slice1 )
fmt.Printf("调用函数后-变量slice2 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice2,len(slice2),cap(slice2),&slice2 )
fmt.Printf("调用函数后-变量slice3 --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice3,len(slice3),cap(slice3),&slice3 )
}
/**输出
变量slice1 --- 值: [1] 长度(len):1 容量(cap): 4 地址: 0xc00000c080
变量slice2 --- 值: [3 4 5] 长度(len):3 容量(cap): 4 地址: 0xc00000c0c0
变量slice3 --- 值: [1 2 3 4 5 6 7] 长度(len):7 容量(cap): 8 地址: 0xc00000c100
调用函数后-变量slice1 --- 值: [100] 长度(len):1 容量(cap): 4 地址: 0xc00000c080
调用函数后-变量slice2 --- 值: [100 4 5] 长度(len):3 容量(cap): 4 地址: 0xc00000c0c0
调用函数后-变量slice3 --- 值: [1 2 3 4 5 6 7] 长度(len):7 容量(cap): 8 地址: 0xc00000c100
*/

append()会改变切片所引用的数组的内容,从而影响到引用同一数组的其他切片。当使用append()追加元素到切片时,如果容量不够(也就是(cap-len) == 0),Go就会创建一个新的内存地址来储存元素。

6.3 使用copy()

package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1,2,3,4}
fmt.Printf("变量slice --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice,len(slice),cap(slice),&slice )

// 定义一个切片变量用来复制slice
copySlice := make([]int,8)

// 使用copy
count := copy(copySlice,slice)
fmt.Printf("复制的数量: %d \n",count)
fmt.Printf("变量copySlice --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
copySlice,len(copySlice),cap(copySlice),&copySlice )

// 修改复制后的切片,源切片不会变
copySlice[0] = 100
fmt.Printf("修改后-->变量slice --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
slice,len(slice),cap(slice),&slice )
fmt.Printf("修改后-->变量copySlice --- 值: %v 长度(len):%d 容量(cap): %d 地址: %p \n",
copySlice,len(copySlice),cap(copySlice),&copySlice )
}
/**输出
变量slice --- 值: [1 2 3 4] 长度(len):4 容量(cap): 4 地址: 0xc00000c080
复制的数量: 4
变量copySlice --- 值: [1 2 3 4 0 0 0 0] 长度(len):8 容量(cap): 8 地址: 0xc00000c0c0
修改后-->变量slice --- 值: [1 2 3 4] 长度(len):4 容量(cap): 4 地址: 0xc00000c080
修改后-->变量copySlice --- 值: [100 2 3 4 0 0 0 0] 长度(len):8 容量(cap): 8 地址: 0xc00000c0c0
*/