Go切片教程展示了如何在Golang中使用切片。
数组是单一数据类型元素的集合。数组包含固定数量的元素,并且不能增长或收缩。数组的元素通过索引访问。
slice是对数组元素的动态大小、灵活的视图。切片可以在底层数组的范围内增长和收缩。Aslice不存储任何数据,它只是描述数组的一部分。
去声明切片
var s []T
我们声明了一个T
类型的切片。切片的声明就像一个数组,只是我们没有在方括号[]
中指定任何大小。
$ go version go version go1.18.1 linux/amd64
我们使用Go版本1.18。
去切片文字
可以使用切片文字创建切片。
package main import "fmt" func main() { var s1 = []int{2, 5, 6, 7, 8} s2 := []int{3, 5, 1, 2, 8} fmt.Println("s1:", s1) fmt.Println("s2:", s2) }
在代码示例中,我们创建了两个切片。
var s1 = []int{2, 5, 6, 7, 8}
右边的表达式是一个切片字面量。
s2 := []int{3, 5, 1, 2, 8}
这里我们有一个速记等价物。
$ go run literal.go s1: [2 5 6 7 8] s2: [3 5 1 2 8]
Goslicemake函数
我们可以使用make
内置函数在Go中创建新的切片。
func make([]T, len, cap) []T
make
函数接受一个类型、一个长度和一个可选的容量。它分配一个大小等于给定容量的底层数组,并返回一个引用该数组的切片。
package main import "fmt" func main() { vals := make([]int, 5) fmt.Println("vals: ", vals) vals[0] = 1 vals[1] = 2 vals[2] = 3 vals[3] = 4 vals[4] = 5 fmt.Println("vals: ", vals) }
我们使用make
函数创建一个大小为5的整数切片。最初,切片的元素全为零。然后我们为切片元素分配新值。
$ go run make_fun.go vals: [0 0 0 0 0] vals: [1 2 3 4 5]
Go切片长度和容量
len
函数返回切片中的元素数。cap
函数返回切片的容量。aslice的容量是底层数组中元素的数量,从切片中的第一个元素开始计数。
package main import "fmt" func main() { vals := make([]int, 5, 10) n := len(vals) c := cap(vals) fmt.Printf("The size is: %d\n", n) fmt.Printf("The capacity is: %d\n", c) vals2 := vals[0:4] n2 := len(vals2) c2 := cap(vals2) fmt.Printf("The size is: %d\n", n2) fmt.Printf("The capacity is: %d\n", c2) }
在代码示例中,我们打印了两个切片的大小和容量。
$ go run len_cap.go The size is: 6 The capacity is: 6 The size is: 4 The capacity is: 6
去切片数组或切片
我们可以通过对现有数组或切片进行切片来创建切片。
为了形成切片,我们指定了一个下限和一个上限:a[low:high]
。这将选择一个半开范围,其中包括第一个元素,但不包括最后一个元素。
我们可以省略上限或下限以使用它们的默认值。下限默认为零,上限默认为切片长度。
package main import "fmt" func main() { vals := [...]int{1, 2, 3, 4, 5, 6, 7} s1 := vals[1:4] fmt.Printf("s1: %v, cap: %d\n", s1, cap(s1)) s2 := vals[5:7] fmt.Printf("s2: %v, cap: %d\n", s2, cap(s2)) s3 := vals[:4] fmt.Printf("s3: %v, cap: %d\n", s3, cap(s3)) s4 := vals[2:] fmt.Printf("s4: %v, cap: %d\n", s4, cap(s4)) s5 := vals[:] fmt.Printf("s5: %v, cap: %d\n", s5, cap(s5)) }
我们从整数数组创建切片。
vals := [...]int{1, 2, 3, 4, 5, 6, 7}
创建了一个整数数组。使用...
运算符,Go会为我们计算数组的大小。
s2 := vals[5:7] fmt.Printf("s2: %v, cap: %d\n", s2, cap(s2))
我们从vals
数组创建一个切片。结果切片包含从索引5到索引7的元素;上限是非包容性的。
$ go run slicing.go s1: [2 3 4], cap: 6 s2: [6 7], cap: 2 s3: [1 2 3 4], cap: 7 s4: [3 4 5 6 7], cap: 5 s5: [1 2 3 4 5 6 7], cap: 7
Go切片迭代
使用for循环,我们可以在Go中迭代切片元素。
package main import "fmt" func main() { words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"} for idx, word := range words { fmt.Println(idx, word) } }
在代码示例中,我们使用for/range
语句遍历一段单词。
$ go run iteration.go 0 falcon 1 bold 2 bear 3 sky 4 cloud 5 ocean
在下面的示例中,我们使用经典的for循环迭代切片。
package main import "fmt" func main() { words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"} for i := 0; i < len(words); i++ { fmt.Println(words[i]) } }
我们使用for
语句遍历一段单词。
$ go run iteration2.go falcon bold bear sky cloud ocean
去切片追加
内置的append
函数将新元素附加到切片。
func append(s []T, vs ...T) []T
第一个参数是T
类型的切片,其余参数是要附加到切片的T
值。
append的结果值是一个切片,其中包含原始切片的所有元素加上提供的值。如果后备数组太小而无法容纳所有给定值,则会分配一个更大的数组。返回的切片将指向新分配的数组。
package main import "fmt" func main() { vals := make([]int, 3) fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals)) fmt.Println("---------------------------") vals = append(vals, 1) vals = append(vals, 2) vals = append(vals, 3) vals = append(vals, 4, 5, 6) fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals)) }
在代码示例中,我们将新元素附加到一个已经有三个元素的切片。
vals := make([]int, 3)
首先,我们创建一个切片,其中三个元素初始化为0。
vals = append(vals, 1) vals = append(vals, 2) vals = append(vals, 3) vals = append(vals, 4, 5, 6)
我们将六个值附加到切片。onego中可以追加多个元素。
$ go run appending.go slice: [0 0 0]; len: 3; cap: 3 --------------------------- slice: [0 0 0 1 2 3 4 5 6]; len: 9; cap: 12
在幕后,Go扩大了底层数组以包含所有新元素。
package main import ( "fmt" "strings" ) func main() { words := []string{} words = append(words, "an") words = append(words, "old") words = append(words, "falcon") res := strings.Join(words, " ") fmt.Println(res) }
我们从一个空字符串切片开始。我们将三个词附加到切片中。然后我们使用strings.Join
函数连接单词,同时在单词之间插入一个空格。
$ go run main.go an old falcon
去切片复制
内置的copy
函数复制一个切片。
func copy(dst, src []T) int
该函数返回复制的元素数。
package main import "fmt" func main() { vals := []int{1, 2, 3, 4, 5} vals2 := make([]int, len(vals)) n := copy(vals2, vals) fmt.Printf("%d elements copied\n", n) fmt.Println("vals:", vals) fmt.Println("vals2:", vals2) }
在代码示例中,我们复制了一段整数。
$ go run copying.go 5 elements copied vals: [1 2 3 4 5] vals2: [1 2 3 4 5]
去切片删除元素
没有从切片中删除项目的内置函数。我们可以使用append
函数进行删除。
package main import ( "fmt" ) func main() { words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"} fmt.Println(words) words = append(words[1:2], words[2:]...) fmt.Println(words) words = append(words[:2], words[4:]...) fmt.Println(words) }
在代码示例中,我们从切片中删除一个元素,然后删除两个元素。
words = append(words[1:2], words[2:]...)
这会从切片中移除第一个元素。我们通过附加两个切片并省略要删除的切片来完成删除。
$ go run remove_elements.go [falcon bold bear sky cloud ocean] [bold bear sky cloud ocean] [bold bear ocean]
去切片独特的元素
在下一个示例中,我们生成一个包含唯一元素的切片。
package main import "fmt" func uniq(vals []int) []int { uvals := []int{} seen := make(map[int]bool) for _, val := range vals { if _, in := seen[val]; !in { seen[val] = true uvals = append(uvals, val) } } return uvals } func main() { vals := []int{1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 9, 9} uvals := uniq(vals) fmt.Printf("Original slice: %v\n", vals) fmt.Printf("Unique slice: %v\n", uvals) }
我们有一个包含重复元素的切片。我们创建了一个仅包含唯一元素的新切片。
seen := make(map[int]bool)
为了完成这项任务,我们创建了一个映射,为我们在切片中遇到的所有值存储一个布尔值true。
for _, val := range vals { if _, in := seen[val]; !in { seen[val] = true uvals = append(uvals, val) } }
我们遍历切片中可能存在重复的元素。如果它不存在于seen
映射中,我们将其存储在那里并将其附加到新的uvals
切片。否则,我们将跳过if块。
$ go run uniq_elems.go Original slice: [1 2 2 3 4 4 5 6 7 8 8 8 9 9] Unique slice: [1 2 3 4 5 6 7 8 9]
去切片排序
Go包含用于对切片进行排序的sort
包。
package main import ( "fmt" "sort" ) func main() { words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"} vals := []int{4, 2, 1, 5, 6, 8, 0, -3} sort.Strings(words) sort.Ints(vals) fmt.Println(words) fmt.Println(vals) }
在代码示例中,我们对一片单词和整数进行排序。
$ go run sorting.go [bear bold cloud falcon ocean sky] [-3 0 1 2 4 5 6 8]
Go切片初值
切片的默认零值为nil
。nil
切片的长度和容量为0,并且没有底层数组。
当我们使用make
函数创建切片时,所有元素都被初始化为0。
package main import "fmt" func main() { var vals []int if vals == nil { fmt.Printf("slice is nil\n") } fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals)) fmt.Println("---------------------------") var vals2 = make([]int, 5) fmt.Printf("slice: %v; len: %d; cap: %d \n", vals2, len(vals2), cap(vals2)) }
我们创建了两个具有默认零值和make-functioninitializedelements的切片。
$ go run initial.go slice is nil slice: []; len: 0; cap: 0 --------------------------- slice: [0 0 0 0 0]; len: 5; cap: 5
Goslice是引用类型
切片是Go中的引用类型。这意味着当我们将引用分配给新变量或将切片传递给函数时,将复制对切片的引用。
package main import "fmt" func main() { vals := []int{ 1, 2, 3, 4, 5, 6 } vals2 := vals vals2[0] = 11 vals2[1] = 22 fmt.Println(vals) fmt.Println(vals2) }
在代码示例中,我们定义了一个切片并将切片分配给一个新变量。通过第二个变量所做的更改反映在原始切片中。
$ go run reftype.go [11 22 3 4 5 6] [11 22 3 4 5 6]
原始切片也被修改。
切片切片
Go切片还可以包含其他切片。
package main import "fmt" func main() { words := [][]string{ {"sky", "ocean"}, {"red", "blue"}, {"C#", "Go"}, } fmt.Printf("slice: %v; len: %d; cap: %d \n", words, len(words), cap(words)) }
该示例创建了一片切片。
$ go run sliceofslices.go slice: [[sky ocean] [red blue] [C# Go]]; len: 3; cap: 3
过滤结构切片
在下一个例子中,我们过滤了一段Go结构。
package main import "fmt" type User struct { name string occupation string country string } func main() { users := []User{ {"John Doe", "gardener", "USA"}, {"Roger Roe", "driver", "UK"}, {"Paul Smith", "programmer", "Canada"}, {"Lucia Mala", "teacher", "Slovakia"}, {"Patrick Connor", "shopkeeper", "USA"}, {"Tim Welson", "programmer", "Canada"}, {"Tomas Smutny", "programmer", "Slovakia"}, } var programmers []User for _, user := range users { if isProgrammer(user) { programmers = append(programmers, user) } } fmt.Println("Programmers:") for _, u := range programmers { fmt.Println(u) } } func isProgrammer(user User) bool { return user.occupation == "programmer" }
在代码示例中,我们定义了一部分用户。我们创建一个仅包含程序员的新切片。
type User struct { name string occupation string country string }
User
结构具有三个字段。
users := []User{ {"John Doe", "gardener", "USA"}, {"Roger Roe", "driver", "UK"}, {"Paul Smith", "programmer", "Canada"}, {"Lucia Mala", "teacher", "Slovakia"}, {"Patrick Connor", "shopkeeper", "USA"}, {"Tim Welson", "programmer", "Canada"}, {"Tomas Smutny", "programmer", "Slovakia"}, }
这是User
结构的原始切片。
var programmers []User
过滤后的用户/程序员存储在程序员
切片中。
for _, user := range users { if isProgrammer(user) { programmers = append(programmers, user) } }
我们遍历users
切片,仅当用户满足isProgrammer
谓词时,才将当前用户添加到programmers
切片。
func isProgrammer(user User) bool { return user.occupation == "programmer" }
对于occupation
字段等于“程序员”的所有用户,IsProgrammer
谓词返回true。
$ go run filter_slice_structs.go Programmers: {Paul Smith programmer Canada} {Tim Welson programmer Canada} {Tomas Smutny programmer Slovakia}
在本教程中,我们使用了Golang中的切片。
列出所有Go教程。