切片
十一、切片
Go 语言切片是对数组的抽象,因此切片是引用类型。但自身是结构体,值拷贝传递。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组
相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片本身不拥有任何数据,它是对现有数组的引用。
切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
1 切片的定义
你可以声明一个未指定大小的数组来定义切片,切片不需要说明长度。
var 变量名 []类型
// 比如 var str []string
// var arr []int。
// []括号内什么都不写 就是切片类型
或使用 make() 函数来创建切片:
var slice []type = make([]type, len)
// 也可以简写,其中 capacity 为可选参数。这里 len 是数组的长度并且也是切片的初始长度
slice := make([]type, len)
slice := make([]type, length, capacity)
2 创建切片的各种方式
func main() {
//1.声明切片
var s1 []int
if s1 == nil {
fmt.Println("是空")
} else {
fmt.Println("不是空")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4.初始化赋值
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5.从数组切片
arr := [5]int{1, 2, 3, 4, 5} // 定义数组
var s6 []int // 定义切片
// 索引区间前闭后开
s6 = arr[1:4]
fmt.Println(s6)
}
一个切片在未初始化之前默认为 nil,长度为 0。
3 切片初始化
s :=[] int {1,2,3 }
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。
var arr = [...]int{0,1,2,3,4,5,6,7,8,9,}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1]
举例:
var arr = [...]int{0,1,2,3,4,5,6,7,8,9,}
var slice0 []int = arr[2:8] // 左闭右开,len=high-low
var slice1 []int = arr[0:6] //0可以省略: var slice []int = arr[:end]
var slice2 []int = arr[5:10] //如果切片到结尾,可以省略: var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素
fmt.Printf("arr %v\n", arr)
fmt.Printf("slice0 %v\n", slice0)
fmt.Printf("slice1 %v\n", slice1)
fmt.Printf("slice2 %v\n", slice2)
fmt.Printf("slice3 %v\n", slice3)
fmt.Printf("slice4 %v\n", slice4)
// 输出:
arr [0 1 2 3 4 5 6 7 8 9]
slice0 [2 3 4 5 6 7]
slice1 [0 1 2 3 4 5]
slice2 [5 6 7 8 9]
slice3 [0 1 2 3 4 5 6 7 8 9]
slice4 [0 1 2 3 4 5 6 7 8]
4 len() 和 cap() 函数
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
以下为具体实例:
package main
import "fmt"
func main() {
var numbers = make([]int,3,5)
printSlice(numbers)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
// 输出
len=3 cap=5 slice=[0 0 0]
5 追加切片元素
正如我们已经知道数组的长度是固定的,它的长度不能增加。 切片是动态的,使用 append
可以将新元素追加到切片上。append 函数的定义是
func append(s[]T,x ... T)[]T
// x … T 在函数定义中表示该函数接受参数 x 的个数是可变的。这些类型的函数被称为[可变函数]。
// append :向 slice 尾部添加数据,返回新的 slice 对象。
如果切片由数组支持,并且数组本身的长度是固定的,那么切片如何具有动态长度。以及内部发生了什么?
当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。
新切片的容量是旧切片的两倍。
举例:
cars := []string{"Ferrari", "Honda", "Ford"}
fmt.Println("cars:", cars, "has old length", len(cars), "and capacity", cap(cars)) // capacity of cars is 3
cars = append(cars, "Toyota")
fmt.Println("cars:", cars, "has new length", len(cars), "and capacity", cap(cars)) // capacity of cars is doubled to 6
// 输出
cars: [Ferrari Honda Ford] has old length 3 and capacity 3
cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6
6 切片的长度和容量
切片的长度是切片中的元素数。
切片的容量是从创建切片索引位置开始的底层数组中元素数。
7 copy() 函数和内存优化
切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。在内存管理方面,这是需要注意的。让我们假设我们有一个非
常大的数组,我们只想处理它的一小部分。然后,我们由这个数组创建一个切片,并开始处理切片。这里需要重点注意的是,在切片引用
时数组仍然存在内存中。
一种解决方法是使用 [copy] 函数 func copy(dst,src[]T)int
来生成一个切片的副本。这样我们可以使用新的切片,原始数组可以被
垃圾回收。
func countries() []string {
countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2] // 创建一个去掉尾部 2 个元素的切片
countriesCpy := make([]string, len(neededCountries))
copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
return countriesCpy
}
func main() {
countriesNeeded := countries()
fmt.Println(countriesNeeded)
}
//现在 countries 数组可以被垃圾回收, 因为 neededCountries 不再被引用。
8 切片的函数传递
切片在内部可由一个结构体类型表示。这是它的表现形式,
type slice struct {
Length int
Capacity int
ZerothElement *byte
}
切片包含长度、容量和指向数组第零个元素的指针。当切片传递给函数时,即使它通过值传递,指针变量也将引用相同的底层数组。因
此,当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见。
本文来自博客园,作者:yyyz,转载请注明原文链接:https://www.cnblogs.com/yyyzyyyz/p/15450205.html