Golang教程 - 初学者指南 分片实例

136 阅读11分钟

这篇博文涵盖了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