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教程。
