这篇博文涵盖了Golang Slice的完整教程与实例
Golang Slice简介
数组是静态固定大小的。切片是动态大小的,这意味着数组一旦创建就不能改变其大小。切片可以改变大小,并且
与数组相比,它提供了更多的功能和灵活性。
我已经发表了一篇关于Golang中数组的文章。
Slice的关键点
- Slice是一个动态数组,可以增加它的大小
- 切片与其他编程语言中的数组相似--Go
- Slice不包含自己的数据,而是在内部使用一个数组。
- 在内部,Slice使用Array点来引用它,有更多的权力和灵活性。
- 切片的元素是可索引的
切片变量的创建和初始化
我们有很多方法可以创建一个Slice变量并初始化它
切片空值
空片可以用分配给它的空数据来创建。这被用来表示空的集合,如数据库查询没有找到结果。
emptySlice:= []int{} // variable value is []
在这里,变量被声明并以空值初始化。在这里,内部数组被创建,空片在len()和cap()函数中返回0。这里是一个完整的空片零值例子。
package main
import (
"fmt"
)
func main() {
var emptySlice = []int{}
fmt.Println(emptySlice, emptySlice == nil, len(emptySlice), cap(emptySlice))
}
上述程序的输出是
非零值的空片
使用空的长度和类型创建了Nil slice。创建的片子的值没有被初始化,它被称为slice nil。这里没有创建内部数组
这将被用来表示一个不存在于函数调用中的片断。
var variable []int //// variable value is nil
Nil slice的长度和容量总是返回0。下面是一个完整的零值slice Nil的例子
package main
import (
"fmt"
)
func main() {
var nilSlice []int
fmt.Println(nilSlice, nilSlice == nil, len(nilSlice), cap(nilSlice))
}
上述程序的一个输出是
使用字面意义创建并初始化切片
这是用literal语法创建一个slice的方法之一 这里是slice literal的语法
// Slice Creation using slice literal
var strs = []string{"one", "two", "three"}
fmt.Println(strs) //[one two three]
在这里,我们使用片断字面创建片断字符串。右手边包含切片字面。Literal是一个表达式,每个表达式指定一个切片元素,用大括号[]括起来。当一个变量被创建时,字面意义。变量被创建并分配了这个值。这里length是空的,编译器从slice元素的数量中推断出长度。上面的片断字面是用速记变量声明语法重写的
strs := []string{"one", "two", "three"}
这里使用短的赋值运算符--:=,省略var关键字。
下面是一个使用片断字面创建的例子
package main
import (
"fmt"
)
func main() {
// Slice Creaation using slice literal
var strs = []string{"one", "two", "three"}
fmt.Println(strs)
// Slice Creaation using shorthand variable declaration
strs1 := []string{"one", "two", "three"}
fmt.Println(strs1)
}
输出是
[one two three]
[one two three]
从一个数组创建一个片断--低和高的表达式
可以使用一个数组的一个片段来创建切片。切片是用冒号隔开的low和high索引来创建的。
下面是一个语法
使用一个数组来创建切片k,指定低位和高位,用冒号隔开,低位指定一个切片的起始索引,高位是一个切片的结束索引,即数组的长度-1,例如
array := [5]int{1,2,3,4,5}
s1:= array[0:5] // [1,2,3,4,5]
上面这一行从一个数组中创建了一个片断。这个片断包含了一个数组的所有值,从low=0到high=len() -1开始。
low和high的索引是可选的。low的默认值总是0,high是一个数组的长度-1,同样可以通过省略low和high的索引重写。
array[0:] same as array[0:len(array)]
array[:5] same as array[0:5]
array[:] same as array[0:len(array)]
下面是一个从数组中创建片断的完整例子
package main
import (
"fmt"
)
func main() {
array := [6]int{1, 2, 3, 4, 5, 6} // array creation
s1 := array[1:5] // Slice creation from array
s2 := array[:2] // Slice creation from array
s3 := array[1:] // Slice creation from array
s4 := array[:] // Slice creation from array
fmt.Println("oringal array: ", array)
fmt.Println("s1 = ", s1)
fmt.Println("s2 = ", s2)
fmt.Println("s3 = ", s3)
fmt.Println("s4 = ", s4)
}
输出是
oringal array: [1 2 3 4 5 6]
s1 = [2 3 4 5]
s2 = [1 2]
s3 = [2 3 4 5 6]
s4 = [1 2 3 4 5 6]
使用其他片断创建片断
这是另外一种从现有片断创建片断的方法。当你想创建切片的一部分并将其分配给其他切片时,这将很有用。
package main
import (
"fmt"
)
func main() {
originalSlice := []string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}
copySlice1 := originalSlice[2:]
copySlice2 := copySlice1[1:3]
copySlice3 := originalSlice[:]
copySlice4 := originalSlice[:4]
fmt.Println("originalSlice : ", originalSlice)
fmt.Println("copySlice1 : ", copySlice1)
fmt.Println("copySlice2 : ", copySlice2)
fmt.Println("copySlice3 : ", copySlice3)
fmt.Println("copySlice4 : ", copySlice4)
}
上述程序的一个输出是
originalSlice : [one two three four five six seven eight nine ten]
copySlice1 : [three four five six seven eight nine ten]
copySlice2 : [four five]
copySlice3 : [one two three four five six seven eight nine ten]
copySlice4 : [one two three four]
使用make()函数创建一个分片
可以使用内置的make()函数来创建slice 下面是make()函数的一个语法
func make([]T, len, cap) []T
这个函数接受数据类型数组、长度和容量,在内部创建数组并返回引用内部数组的slice。容量是可选的。基于参数的第一种类型,非零值被打印出来(对于数字 - 0,字符串 - 空格),例如
slice1 := make([]int, 4)
fmt.Printf("length=%d capacity=%d slice=%v\n",len(slice1), cap(slice1), slice1) // length=4 capacity=4 slice=[0 0 0 0]
这创建了一个具有数组整数非零值的slice,长度=4,容量=4,当容量没有指定时,默认等于长度。 提供了容量,底层数组被创建为容量6,数据被存储到长度=3。
slice2 := make([]int, 3, 6)
fmt.Printf("length=%d capacity=%d slice=%v\n",len(slice2), cap(slice2), slice2) //length=3 capacity=6 slice=[0 0 0]
一个完整的slice make函数的例子
package main
import (
"fmt"
)
func main() {
slice1 := make([]int, 4)
fmt.Printf("length=%d capacity=%d slice=%v\n",
len(slice1), cap(slice1), slice1)
slice2 := make([]int, 3, 6)
fmt.Printf("length=%d capacity=%d slice=%v\n",
len(slice2), cap(slice2), slice2)
slice3 := slice2[:2]
fmt.Printf("length=%d capacity=%d slice=%v\n",
len(slice3), cap(slice3), slice3)
}
输出是
length=4 capacity=4 slice=[0 0 0 0]
length=3 capacity=6 slice=[0 0 0]
length=2 capacity=6 slice=[0 0]
长度和容量切片 - len() capacity() 函数
Golang有内置的函数,如len()和capacity()函数 slice并不保存自己的数据,它是对一个底层内部数组的引用或指针。 len()函数- length是slice中元素的数量 cap函数- capacity是底层数组中元素的数量,从起始索引开始计算
package main
import (
"fmt"
)
func main() {
// Slice Creation using slice literal
var strs = [8]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}
s1 := strs[1:3]
displaySlice(s1)
}
func displaySlice(s []string) {
fmt.Printf(" %v length:%d capacity:%d\n", s, len(s), cap(s))
}
输出是
[two three] length:2 capacity:7
如果它的长度超过了现有的容量,它就会抛出运行时异常,下面是一个通过改变其长度来扩展/重新切割的例子
package main
import (
"fmt"
)
func main() {
// Slice Creation using slice literal
var strs = []string{"one", "two", "three", "four", "five", "six", "seven", "eight"}
displaySlice(strs)
// Empty Slice
s1 := strs[:0]
displaySlice(s1)
// Shirnk slice length.
s2 := strs[:4]
displaySlice(s2)
// Remove first three values
s3 := strs[3:]
displaySlice(s3)
}
func displaySlice(s []string) {
fmt.Printf("%v length:%d capacity:%d\n", s, len(s), cap(s))
}
输出是
[one two three four five six seven eight] length:8 capacity:8
[] length:0 capacity:8
[one two three four] length:4 capacity:8
[four five six seven eight] length:5 capacity:5
分片中元素的迭代
像数组一样,使用for循环和带有范围关键字的for对元素进行迭代。
遍历切片中的元素--for循环
这种迭代的工作方式与数组类似,在片断的长度上执行若干次迭代。
package main
import (
"fmt"
)
func main() {
numbs := []string{"one", "two", "three", "four"}
for i := 0; i < len(numbs); i++ {
fmt.Println(numbs[i])
}
}
输出是
使用带范围关键字的for循环对切片中的元素进行迭代
使用范围for循环,每次迭代都会返回两个元素,第一个元素是索引,第二个是实际的元素值,如果一个片断是nil,则会评估零返回。
package main
import (
"fmt"
)
func main() {
numbs := []string{"one", "two", "three", "four"}
for index, numb := range numbs {
fmt.Printf("%d = %s\n", index+1, numb)
}
}
输出是
1 = one
2 = two
3 = three
4 = four
你可以在for range循环中使用空白标识符省略索引或值,每个迭代都有索引和值,如果你不想要这些,你可以用空白标识符(_)代替。编译器认为这个值是不需要的,同样可以用一个空白标识符重写为
func main() {
numbs := []string{"six", "seven", "eight", "nine"}
for _, numb := range numbs {
fmt.Printf("%s\n", numb)
}
}
输出为
six
seven
eight
nine
复制函数--复制切片中的元素
copy()是slice的一个内置函数,将元素从一个slice复制到另一个slice。
func copy(destination, source []T) int
这个函数有两个slice类型的参数,所有的源元素都被复制到目标元素,如果元素已经存在,则覆盖这些元素。它返回复制的元素数量,即len(source)和len(destination)的最小值。
复制片子到另一个片子
复制src和destination片,并返回destination片。
package main
import "fmt"
func main() {
destination := make([]string, 4)
source := []string{"one", "two", "three", "four"}
fmt.Println("source: ", source)
fmt.Println("destination: ", destination)
output := copy(destination, source)
fmt.Println("Number of elements copied = ", output)
fmt.Println("Output of Copy = ", destination)
}
输出为
source: [one two three four]
destination: [ ]
Number of elements copied = 4
Output of Copy = [one two three four]
复制字符串字节到字节片的例子
分片拷贝函数也可以用来将字符串字节拷贝到字节片中。
package main
import "fmt"
func main() {
var destination = make([]byte, 5)
source := "Test string"
fmt.Println("source: ", source)
fmt.Println("destination: ", destination)
output := copy(destination, source)
fmt.Println("Number of elements copied = ", output)
fmt.Println("Output of Copy = ", destination)
}
输出是
source: Test string
destination: [0 0 0 0 0]
Number of elements copied = 5
Output of Copy = [84 101 115 116 32]
Append函数--将一个元素添加到Slice的末端元素上
Slice是动态大小,在运行时改变其长度。Append函数是Slice中的一个内置函数,它可以在slice的末端添加元素。下面是一个Append函数的签名
func append(inputslice []T, args ...T) []T
这个函数接收输入的slice元素args ...T - 这是一个包含多个参数的变量函数,并返回所有元素的新slice,包括现有的元素,如果下划线数组有足够的容量,元素被添加到最后一个元素的末端,长度被增加,如果没有足够的容量,新的数组将被创建,所有的现有值被复制到新数组,新元素被添加到现有元素的末端,新数组被slice引用并返回
package main
import "fmt"
func main() {
numbs := []string{"one", "two", "three", "four"}
output := append(numbs, "five", "six", "seven")
fmt.Printf(" %v, length = %d, capacity = %d\n", numbs, len(numbs), cap(numbs))
fmt.Printf(" %v, length = %d, capacity = %d\n", output, len(output), cap(output))
numbs[0] = "newone"
numbs[1] = "newtwo"
fmt.Printf(" %v, length = %d, capacity = %d\n", numbs, len(numbs), cap(numbs))
fmt.Printf(" %v, length = %d, capacity = %d\n", output, len(output), cap(output))
}
输出是
[one two three four], length = 4, capacity = 4
[one two three four five six seven], length = 7, capacity = 8
[newone newtwo three four], length = 4, capacity = 4
[one two three four five six seven], length = 7, capacity = 8
在上面的例子中,slice numbs的容量为4,当你添加更多的元素时,它不能增加其大小。新的下划线数组以更大的尺寸创建,复制现有的元素并追加新的元素,新的分片容量为8。
将分片附加到另一个分片上
使用三点符号(...)操作符,可以将一个分片追加到另一个分片上,追加函数的输出结果是不重叠的
package main
import "fmt"
func main() {
s1 := []string{"one", "two", "three"}
s2 := []string{"four", "five", "six"}
s3 := append(s1, s2...)
fmt.Println("s1 = ", s1)
fmt.Println("s2 = ", s2)
fmt.Println("Output append = ", s3)
}
输出是
s1 = [one two three]
s2 = [four five six]
Output append = [one two three four five six]
将字符串字节附加到切片字节上
使用append函数(),我们可以将一个字符串追加到字节片中。
package main
import "fmt"
func main() {
s1 := []byte("one")
s2 := "six"
s3 := append(s1, s2...)
fmt.Println("s1 = ", s1)
fmt.Println("s2 = ", s2)
fmt.Println("Output append = ", s3)
}
输出为
s1 = [111 110 101]
s2 = six
Output append = [111 110 101 115 105 120]
修改一个slice元素
Slice是一个内部数组的引用,它本身没有任何数据。对slice所做的任何修改都会在内部数组中改变。
package main
import "fmt"
func main() {
slice := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
newslice := slice[2:4]
fmt.Println("Before Modification", slice)
for k := range newslice {
newslice[k]++
}
fmt.Println("After Modification", slice)
}
输出为
Before Modification [1 2 3 4 5 6 7 8 9 0]
After Modification [1 2 4 5 5 6 7 8 9 0]
创建一个有10个元素的slice,通过复制原slice的索引2,3来创建新的slice 使用范围循环对新slice进行迭代,并增加其原始值。观察到原始片断中的增量值
多维片断--片断的片断
和数组一样,切片也可以是多维类型的。正常的切片元素可以是任何类型的。每个类型也可以包含切片。这与数组的工作原理相同 以下是一个多维切片的例子
package main
import "fmt"
func main() {
multiSlice := [][]string{
{"one", "two", "three"},
{"four", "five", "six"},
{"seven", "eight", "nine"},
{"ten", "eleven", "Twelve"},
}
fmt.Println("Slice of slice :", multiSlice)
fmt.Println("length : ", len(multiSlice))
fmt.Println("capacity : ", cap(multiSlice))
}
当上述程序被编译后,输出如下
Slice of slice : [[one two three] [four five six] [seven eight nine] [ten eleven Twelve]]
length : 4
capacity : 4