目录
[1、append() 函数](#1、append() 函数)
[三、make() 函数构造切片](#三、make() 函数构造切片)
一、什么是切片
切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。
每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。
切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型。
这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(左闭右开的区间)。
Go语言中切片的内部结构包含地址、大小和容量,切片一般用于快速地操作一块数据集合。
1、从连续内存区域生成切片
格式如下:
slice [开始位置 : 结束位置]
语法说明如下:
- slice:表示目标切片对象;
- 开始位置:对应目标切片对象的索引;
- 结束位置:对应目标切片的结束索引。
2、从数组生成切片
代码如下:
var a = [3]int{1, 2, 3}
//a[1:2] 生成了一个新的切片
fmt.Println(a, a[1:2]) //[1 2 3],[2]
从数组或切片生成新的切片拥有如下特性:
- 取出的元素数量为:结束位置 - 开始位置;
- 取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取;
- 当缺省开始位置时,表示从连续区域开头到结束位置
(a[:2])
; - 当缺省结束位置时,表示从开始位置到整个连续区域末尾
(a[0:])
; - 两者同时缺省时,与切片本身等效
(a[:])
; - 两者同时为 0 时,等效于空切片,一般用于切片复位
(a[0:0])
。
注意:超界会报运行时错误,比如数组长度为3,则结束位置最大只能为3
切片在指针的基础上增加了大小,约束了切片对应的内存区域,切片使用中无法对切片内部的地址和大小进行手动调整,因此切片比指针更安全、强大。
示例
var highRiseBuilding [30]int
for i := 0; i < 30; i++ {
highRiseBuilding[i] = i + 1
}
// 区间
fmt.Println(highRiseBuilding[10:15])
// 中间到尾部的所有元素
fmt.Println(highRiseBuilding[20:])
// 开头到中间指定位置的所有元素
fmt.Println(highRiseBuilding[:2])
二、直接声明新的切片
除了可以从原有的数组或者切片中生成切片外,也可以声明一个新的切片
,每一种类型都可以拥有其切片类型
,表示多个相同类型元素的连续集合。
切片类型声明格式如下:
//name 表示切片的变量名,Type 表示切片对应的元素类型。
var name []Type
// 声明字符串切片
var strList []string
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{} //赋了空值
// 输出3个切片
fmt.Println(strList, numList, numListEmpty)
// 输出3个切片大小
fmt.Println(len(strList), len(numList), len(numListEmpty))
// 切片判定空的结果
fmt.Println(strList == nil) //true
fmt.Println(numList == nil) //true
fmt.Println(numListEmpty == nil) //true
1、append() 函数
切片是动态结构,只能与 nil 判定相等,不能互相判定相等。声明新的切片后,可以使用 append() 函数向切片中添加元素
var strList []string
// 追加一个元素
strList = append(strList,"ms的go教程")
fmt.Println(strList)
三、make() 函数构造切片
如果需要动态地创建一个切片,可以使用 make() 内建函数,格式如下:
make( []Type, size, cap )
-
Type
是指切片的元素类型 -
size
指的是为这个类型已经被分配多少个元素 -
cap
为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题
a := make([]int, 2) b := make([]int, 2, 10) fmt.Println(a, b) //容量不会影响当前的元素个数,因此 a 和 b 取 len 都是 2 //但如果我们给a 追加一个 a的长度就会变为3 a = append(a, 3) fmt.Println(len(a), len(b))
使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。
思考题
var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
myslice := numbers4[4:6]
//这打印出来长度为2
fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice))
myslice = myslice[:cap(myslice)]
//为什么 myslice 的长度为2,却能访问到第四个元素
fmt.Printf("myslice的第四个元素为: %d", myslice[3])
-
长度(len) :切片当前存储的元素个数(可通过
len(myslice)
获取) -
容量(cap) :切片可以扩展到的最大长度(不重新分配内存的情况下,可通过
cap(myslice)
获取)
四、切片复制
Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。
copy() 函数的使用格式如下:
copy( destSlice, srcSlice []T) int
- 其中
srcSlice
为数据来源切片 destSlice
为复制的目标(也就是将 srcSlice 复制到 destSlice)
目标切片必须分配过空间且足够承载复制的元素个数,并且来源和目标的类型必须一致,copy() 函数的返回值表示实际发生复制的元素个数。
下面的代码展示了使用 copy() 函数将一个切片复制到另一个切片的过程:
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
切片的引用和复制操作对切片元素的影响:
package main
import "fmt"
func main() {
// 设置元素数量为1000
const elementCount = 1000
// 预分配足够多的元素切片
srcData := make([]int, elementCount)
// 将切片赋值
for i := 0; i < elementCount; i++ {
srcData[i] = i
}
// 引用切片数据 切片不会因为等号操作进行元素的复制
refData := srcData
// 预分配足够多的元素切片
copyData := make([]int, elementCount)
// 将数据复制到新的切片空间中
copy(copyData, srcData)
// 修改原始数据的第一个元素
srcData[0] = 999
// 打印引用切片的第一个元素 引用数据的第一个元素将会发生变化
fmt.Println(refData[0])
// 打印复制切片的第一个和最后一个元素 由于数据是复制的,因此不会发生变化。
fmt.Println(copyData[0], copyData[elementCount-1])
// 复制原始数据从4到6(不包含)
copy(copyData, srcData[4:6])
for i := 0; i < 5; i++ {
fmt.Printf("%d ", copyData[i])
}
}