05.go切片详细介绍
切片介绍
切片:是一个很小的对象,对底层数组进行了抽象,并提供相关的操作方法。切片有 3 个字段的数据结构,这些数据结构包含 Go 语言需要操作底层数组的元数据
切片创建
package main
import (
"log"
)
type Fruit struct {
Fenshu int
Zhuid int
}
var slice9 []Fruit
//一、切片的声明
func Demo01() {
//1.创建一个字符串切片
// 其长度和容量都是 5 个元素
slice := make([]string, 5)
log.Println(slice)
// 2.创建一个整型切片
// 其长度为 3 个元素,容量为 5 个元素
slice2 := make([]int, 3, 5)
log.Println(slice2)
// 3.创建字符串切片
// 其长度和容量都是 5 个元素
slice3 := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
log.Println(slice3)
// 4.创建字符串切片
// 使用空字符串初始化第 100 个元素
slice4 := []string{99: "0"}
log.Println(slice4)
// 5.创建 nil 整型切片
var slice5 []int
log.Println(slice5)
//6.创建一个slice4的截取切片
slice6 := slice4[1:2]
log.Println(slice6)
// 7.使用 make 创建空的整型切片
slice7 := make([]int, 0)
log.Println(slice7)
//8. 使用切片字面量创建空的整型切片
slice8 := []int{}
log.Println(slice8)
//9.全局结构体切片
slice9 = make([]Fruit, 24)
log.Println(slice9)
}
func main() {
Demo01()
}
切片赋值
对切片里某个索引指向的元素赋值和对数组里某个索引指向的元素赋值的方法完全一样。
package main
import (
"log"
)
type Fruit struct {
Fenshu int
Zhuid int
}
//切片的赋值
func Demo01() {
//选中一个赋值
slice := make([]string, 5)
slice[0] = "test"
log.Println(slice)
//声明赋值
slice1 := []int{10, 20, 30}
log.Println(slice1)
//结构体切片声明赋值
slice2 := make([]Fruit, 3)
slice2 = []Fruit{
Fruit{15, 4},
Fruit{50, 8}}
log.Println(slice2)
}
func main() {
Demo01()
}
切片遍历
遍历整个切片并且添加打印
package main
import (
"log"
)
type Fruit struct {
Fenshu int
Zhuid int
}
func Demo01() {
slice2 := make([]Fruit, 10)
slice2 = []Fruit{
Fruit{1, 14},
Fruit{2, 24},
Fruit{3, 34},
Fruit{4, 44},
Fruit{5, 54},
Fruit{6, 64},
Fruit{7, 74},
Fruit{8, 84},
Fruit{9, 94},
Fruit{10, 104},
Fruit{11, 208}}
log.Println(slice2)
for index, value := range slice2 {
log.Printf("index:%d", index)
log.Println(value)
}
}
func main() {
Demo01()
}
当迭代切片时,关键字 range 会返回两个值。第一个值是当前迭代到的索引位置,第二个
值是该位置对应元素值的一份副本
切片传递
在函数间传递切片就是要在函数间以值的方式传递切片。由于切片的尺寸很小,在函数间复
制和传递切片成本也很低。让我们创建一个大切片,并将这个切片以值的方式传递给函数 foo,
package main
import (
"log"
)
// 函数 foo 接收一个整型切片,并返回这个切片
func foo(slice []int) []int {
log.Println(slice)
slice[1] = 200
return slice
}
func main() {
//创建10个长度切片
slice := make([]int, 10)
log.Println(slice)
slice[0] = 100
// 将 slice 传递到函数 foo
foo(slice)
log.Println(slice)
}
2018/02/07 13:41:06 [0 0 0 0 0 0 0 0 0 0]
2018/02/07 13:41:06 [100 0 0 0 0 0 0 0 0 0]
2018/02/07 13:41:07 [100 200 0 0 0 0 0 0 0 0]
可以看出我们操作的是一个切片。
在 64 位架构的机器上,一个切片需要 24 字节的内存:指针字段需要 8 字节,长度和容量字段分别需要 8 字节。由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片复制到任意函数的时候,对底层数组大小都不会有影响。复制时只会复制切片本身,不会涉及底层数组
在函数间传递 24 字节的数据会非常快速、简单。这也是切片效率高的地方。不需要传递指针和处理复杂的语法,只需要复制切片,按想要的方式修改数据,然后传递回一份新的切片副本。
切片截取/取长度len()/容量cap()
package main
import "log"
func main() {
var numbers = make([]int, 3, 5)
printSlice(numbers)
}
func printSlice(x []int) {
log.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
打印:
len=3 cap=5 slice=[0 0 0]
package main
import "log"
func main() {
/* 创建切片 */
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
printSlice(numbers)
/* 打印原始切片 */
log.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
log.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
log.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
log.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int, 0, 5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int) { log.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x) }
打印
2018/02/07 13:51:44 len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
2018/02/07 13:51:44 numbers == [0 1 2 3 4 5 6 7 8]
2018/02/07 13:51:44 numbers[1:4] == [1 2 3]
2018/02/07 13:51:44 numbers[:3] == [0 1 2]
2018/02/07 13:51:44 numbers[4:] == [4 5 6 7 8]
2018/02/07 13:51:44 len=0 cap=5 slice=[]
2018/02/07 13:51:44 len=2 cap=9 slice=[0 1]
切片append() 和 copy()
package main
import "log"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2, 3, 4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1, numbers)
printSlice(numbers1)
}
func printSlice(x []int) { log.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x) }
控制台
2018/02/07 13:54:22 len=0 cap=0 slice=[]
2018/02/07 13:54:22 len=1 cap=1 slice=[0]
2018/02/07 13:54:22 len=2 cap=2 slice=[0 1]
2018/02/07 13:54:22 len=5 cap=6 slice=[0 1 2 3 4]
2018/02/07 13:54:23 len=5 cap=12 slice=[0 1 2 3 4]
总结:
copy会重新的拷贝一个切片,如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。
append
主要用于给某个切片(slice)追加元素
如果该切片存储空间(cap)足够,就直接追加,长度(len)变长;如果空间不足,就会重新开辟内存,并将之前的元素和新的元素一同拷贝进去
切片多线程操作
小结
Go 语言里切片经常用来处理数据的集合,映射用来处理具有键值对结构的数据。
内置函数 make 可以创建切片和映射,并指定原始的长度和容量。也可以直接使用切片和映射字面量,或者使用字面量作为变量的初始值。
切片有容量限制,不过可以使用内置的 append 函数扩展容量。
映射的增长没有容量或者任何限制。
内置函数 len 可以用来获取切片或者映射的长度。
内置函数 cap 只能用于切片。
通过组合,可以创建多维数组和多维切片。也可以使用切片或者其他映射作为映射的值。但是切片不能用作映射的键