Skip to content

切片

切片的定义

切片是对数组的抽象,提供了更强大和灵活的数据结构。切片本身不存储任何数据,而是引用一个底层数组的一部分。

语法格式

go
// 切片的[]是不能有长度的。
var 变量名 []数据类型
go
// 使用make内置函数创建切片
// 数据类型: Slice、Map、Channel
// 长度: 当前包含的元素个数
// 容量: 当前底层容纳的最大元素个数
make([]数据类型, 长度, 容量)

// 另一种写法
var 变量名 []数据类型 = make([]数据类型, 长度, 容量)
// 也可以简写
变量名 := make([]数据类型, 长度, 容量)

示例

go
package main

import "fmt"

func main() {
	// 定义一个int类型的切片
	var s1 []int
	fmt.Printf("s1的类型:%T\n", s1)

	// 使用make函数定义一个string类型的切片
	s2 := make([]string, 0, 3)
	fmt.Printf("s2的类型:%T\n", s2)
}
go
package main

import "fmt"

func main() {
	// 定义string类型切片
	// 此时的切片为空【栈内存已经分配好切片的指针、长度、容量】
	// 但是堆内存中还没有给切片分配任何存储空间
	var s1 []string // nil
	// 定义int类型切片
	// 这里原理同上【和s1切片同理】
	var s2 []int // nil
	// 定义一个int类型空切片
	s3 := []int{} // {}
	// 打印三个切片
	fmt.Println(s1, s2, s3) // [] [] []
	// 打印三个切片的长度
	fmt.Println(len(s1), len(s2), len(s3)) // 0 0 0
	// 切片判断为空
	fmt.Println(s1 == nil) // true
	fmt.Println(s2 == nil) // true
	fmt.Println(s3 == nil) // false
}
go
package main

import "fmt"

func main() {
	// make函数创建string类型切片
	// 使用 make 创建的切片都不可能是 nil
	s1 := make([]string, 0)
	fmt.Println(len(s1)) // 0
	// 判断make函数创建的切片是否为nil
	fmt.Println(s1 == nil)
}

给切片添加元素

使用 append() 函数是用来向切片末尾追加元素。

语法格式

go
append(切片, 元素1, 元素2, 元素3, ..., 元素n)

示例

go
package main

import "fmt"

func main() {
	// 使用make函数定义一个int类型切片,切片的长度为0,容量为5
	// 此时这个切片是空的,但是这个切片不是nil,空不等同于nil,不等同于nil
	// 空是说明容器是没有东西的,nil是这个容器不存在的
	s1 := make([]int, 0, 5)
	fmt.Println(s1) // 此时的切片是[]

	// 使用append函数向切片中添加元素
	s1 = append(s1, 1, 2, 3, 4)
	fmt.Println(s1) // [1 2 3 4]
}
go
package main

import "fmt"

func main() {
	// 使用append函数给切片末尾追加元素。
	// append函数只能在末尾追加元素,不能在中间【或者指定索引位置】插入元素。
	// 注意append函数会返回一个新切片,所以这里需要重新赋值给s1
	s1 := make([]int, 5, 10) // 这里初始化的切片前面5个元素是0,后面5个元素是nil
	fmt.Printf("s1类型:%T\n", s1)
	fmt.Printf("s1长度:%d,s1容量:%d\n", len(s1), cap(s1)) // len: 5, cap: 10
	fmt.Printf("s1:%v\n", s1)
	fmt.Println("=======================")
	fmt.Println("append()第一次")
	s1 = append(s1, 1, 2, 3, 4, 5) // 5个元素
	fmt.Printf("s1类型:%T\n", s1)
	fmt.Printf("s1长度:%d,s1容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s1:%v\n", s1)
	fmt.Println("=======================")
	fmt.Println("append()第二次") // 第二次追加元素,发现容量不够,自动扩容
	s1 = append(s1, 6, 7, 8)   // 3个元素
	fmt.Printf("s1类型:%T\n", s1)
	fmt.Printf("s1长度:%d,s1容量:%d\n", len(s1), cap(s1)) // len: 13, cap: 20
	fmt.Printf("s1:%v\n", s1)
}

将数组转成切片

语法格式

go
// 索引是左闭右开的【包含起始索引的元素直到结束索引的前一个元素,但不包含结束索引的元素】
// 所以切片的长度是结束索引减去起始索引。
var 变量名 = 数组[起始索引:结束索引]

示例

go
package main

import "fmt"

func main() {
	// 将数组转成切片语法格式:数组名[起始索引:结束索引]
	// 定义一个int类型长度10的数组
	arr1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	// 打印数组信息
	fmt.Printf("len:%d\n", len(arr1)) // len:10
	fmt.Printf("arr1:%T\n", arr1)     // arr1:[10]int

	// 将数组索引为2到5的元素转成切片
	// 这里2到5是左闭右开的等价于[2,5)表示一个开区间,其中包含2但不包含5
	// 长度等于结束索引减去起始索引,所以长度为3
	s1 := arr1[2:5]
	fmt.Printf("len:%d\n", len(s1)) // len:3
	fmt.Printf("s1:%T\n", s1)       // s1:[]int
}
go
package main

import "fmt"

func main() {
	// 将数组转成切片语法格式:数组名[起始索引:结束索引]
	// 创建一个int类型长度10的数组
	arr1 := [10]int{27, 86, 82, 48, 80, 83, 19, 68, 96, 79}
	fmt.Printf("arr1的类型:%T\n", arr1)
	fmt.Printf("arr1的长度:%d,容量:%d\n", len(arr1), cap(arr1))
	fmt.Printf("arr1的内容:%v\n", arr1)
	fmt.Println("=======================================")

	// 将数组转成切片,这里的[:]表示将数组的全部元素拷贝到切片中
	s1 := arr1[:]
	fmt.Printf("s1的类型:%T\n", s1)
	fmt.Printf("s1的长度:%d,容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s1的内容:%v\n", s1)
	fmt.Println("=======================================")

	// 从:左边没有写就是从0开始拷贝元素,直到拷贝到:右边的索引为4位置,索引为5是不包含的.
	s2 := arr1[:5]
	fmt.Printf("s2的类型:%T\n", s2)
	fmt.Printf("s2的长度:%d,容量:%d\n", len(s2), cap(s2))
	fmt.Printf("s2的内容:%v\n", s2)
	fmt.Println("=======================================")

	// 从:右边没有写就是从索引为6位置开始拷贝元素,直到拷贝到数组的最后一个元素.
	s3 := arr1[6:]
	fmt.Printf("s3的类型:%T\n", s3)
	fmt.Printf("s3的长度:%d,容量:%d\n", len(s3), cap(s3))
	fmt.Printf("s3的内容:%v\n", s3)
}

切片拷贝

使用 copy() 函数可以将一个数组切片的元素拷贝到另一个数组切片中。

语法格式

go
copy(目标切片, 源切片) // 返回拷贝的元素个数

示例

go
package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := []int{6, 7, 8}
	// 拷贝之前
	fmt.Printf("s1:%v\n", s1) // [1 2 3 4 5]
	fmt.Printf("s1的长度:%d,s1的容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s2:%v\n", s2) // [6 7 8]
	fmt.Printf("s2的长度:%d,s2的容量:%d\n", len(s2), cap(s2))
	// 将s1拷贝到s2
	copy(s2, s1)
	fmt.Printf("s1:%v\n", s1) // [1 2 3 4 5]
	fmt.Printf("s1的长度:%d,s1的容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s2:%v\n", s2) // [1 2 3]
	fmt.Printf("s2的长度:%d,s2的容量:%d\n", len(s2), cap(s2))
}
go
package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := []int{6, 7, 8}
	// 拷贝之前
	fmt.Printf("s1:%v\n", s1) // [1 2 3 4 5]
	fmt.Printf("s1的长度:%d,s1的容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s2:%v\n", s2) // [6 7 8]
	fmt.Printf("s2的长度:%d,s2的容量:%d\n", len(s2), cap(s2))
	// 将s2拷贝到s1
	copy(s1, s2)
	// 拷贝之后
	fmt.Printf("s1:%v\n", s1) // [6 7 8 4 5]
	fmt.Printf("s1的长度:%d,s1的容量:%d\n", len(s1), cap(s1))
	fmt.Printf("s2:%v\n", s2) // [6 7 8]
	fmt.Printf("s2的长度:%d,s2的容量:%d\n", len(s2), cap(s2))
}

切片中移除元素

在 Go 语言中切片并没有删除元素的方法或者是接口,需要根据切片本身的特性,来删除切片中的元素。

首先介绍的是从开头位置移除[删除]切片中的元素。

示例

go
package main

import "fmt"

func main() {
	// 直接移动数据指针的方式删除切片中的元素
	// 删除开头N个元素语法格式:切片名 = 切片名[N:]
	// 定义切片初始化5个元素
	s1 := []int{1, 2, 3, 4, 5}
	fmt.Println(s1) // [1 2 3 4 5]
	s1 = s1[1:]     // 删除第一个元素【从下标1开始截取到末尾】
	fmt.Println(s1) // [2 3 4 5]
}
go
package main

import "fmt"

func main() {
	// 定义切片
	s1 := []int{1, 2, 3, 4, 5}
	fmt.Println(s1) // [1 2 3 4 5]
	// 通过 append 函数将第一个元素之后的所有元素复制到一个新的切片中,覆盖原切片内容。
	s1 = append(s1[:0], s1[1:]...)
	fmt.Println(s1) // [2 3 4 5]
}

从中间位置移除[删除]切片中的元素。

示例

go
package main

import "fmt"

func main() {
	// 定义一个切片int类型
	s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
	fmt.Println(s1)
	// 移除值为10的元素
	s1 = append(s1[:9], s1[10:]...)
	fmt.Println(s1)
}

从尾部位置移除[删除]切片中的元素。

示例

go
package main

import "fmt"

func main() {
	// 定义切片
	s1 := []int{1, 2, 3, 4, 5}
	fmt.Println(s1)
	// 移除后面一个元素
	s1 = s1[:len(s1)-1]
	fmt.Println(s1)
	// 移除后面N个元素
	s1 = s1[:len(s1)-2]
	fmt.Println(s1)
}

多维切片(了解)

多维切片与多维数组类似,但是多维切片的元素是切片,而不是数组。

语法格式

go
var 多维切片名 [][]...[] 数据类型

示例

go
package main

import "fmt"

func main() {
	// 定义一个二维切片
	twoDimSlice := make([][]int, 3)

	// 为每个一维切片分配内存空间
	for i := range twoDimSlice {
		twoDimSlice[i] = make([]int, 4)
	}

	// 填充数据
	twoDimSlice[0] = []int{1, 2, 3, 4}
	twoDimSlice[1] = []int{5, 6, 7, 8}
	twoDimSlice[2] = []int{9, 10, 11, 12}

	// 访问元素
	fmt.Println(twoDimSlice[1][2]) // 输出: 7
}
go
package main

import "fmt"

func main() {
	// 定义一个三维切片
	threeDimSlice := make([][][]int, 2)

	// 为每个二维切片分配内存空间
	for i := range threeDimSlice {
		threeDimSlice[i] = make([][]int, 3)
		for j := range threeDimSlice[i] {
			threeDimSlice[i][j] = make([]int, 4)
		}
	}

	// 填充数据
	threeDimSlice[0][0] = []int{1, 2, 3, 4}
	threeDimSlice[0][1] = []int{5, 6, 7, 8}
	threeDimSlice[0][2] = []int{9, 10, 11, 12}
	threeDimSlice[1][0] = []int{13, 14, 15, 16}
	threeDimSlice[1][1] = []int{17, 18, 19, 20}
	threeDimSlice[1][2] = []int{21, 22, 23, 24}

	// 访问元素
	fmt.Println(threeDimSlice[1][1][2]) // 输出: 19
}

根据 MIT 许可证发布