go語言中數組的長度是固定的并且數組長度屬于類型的一部分,所以數組有很多的局限性。
go提供了一種更加靈活強悍的內置類型切片(“動態數組”)。
切片(Slice)是一個擁有相同類型元素的可變長度的序列。它是基于數組類型做的一層封裝。它非常靈活,支持自動擴容。
切片是一個引用類型,它的內部結構包含地址
、長度
和容量
。
一、切片的聲明和初始化
1、直接聲明切片
var name []T
其中:name-切片的名稱
???????????T-切片的類型
package main
import (
"fmt"
)
func main() {
var numSlice = []int{1, 3, 5, 7, 9}
fmt.Println(numSlice)
var stringSlice []string
stringSlice = []string{"北京", "上海", "深圳", "蘇州"}
fmt.Println(stringSlice)
}
運行結果
[1 3 5 7 9]
[北京 上海 深圳 蘇州]
2、基于數組創建切片
var slice = arr[low: high]
其中:表達式中的low和high表示一個索引范圍(左包含,右不包含)
package main
import (
"fmt"
)
func main() {
var numArry = [5]int{1, 2, 3, 4, 5}
var numSlice = numArry[2:4]
fmt.Println(numSlice)
}
運行結果
[3 4]
3、使用make()函數構造切片
var slice = make([]T, len, cap)
其中:T-切片的元素類型
???????????len-切片的長度
???????????cap-切片的容量
package main
import (
"fmt"
)
func main() {
var intSlice = make([]int, 2, 4)
fmt.Println(intSlice)
fmt.Println(len(intSlice))
fmt.Println(cap(intSlice))
}
運行結果
[0 0]
2
4
二、切片的本質
切片的本質就是對底層數組的封裝,它包含了三個信息:底層數組的指針、切片的長度(len)和切片的容量(cap)
舉例:
數組 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[0:5],相應示意圖如下:
切片s2 := a[3:6],相應示意圖如下:
對slice所做的任何修改都將反映在底層數組中。
當多個切片共享相同的底層數組時,每個切片對元素所做的更改將在數組中反映出來。
package main
import (
"fmt"
)
func main() {
arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("操作前:", arr)
slice := arr[:5]
fmt.Println("操作前:", slice)
slice[0] = 100
fmt.Println("操作后:", arr)
fmt.Println("操作后:", slice)
}
運行結果
操作前: [0 1 2 3 4 5 6 7]
操作前: [0 1 2 3 4]
操作后: [100 1 2 3 4 5 6 7]
操作后: [100 1 2 3 4]
三、切片的操作
1、切片的遍歷
切片的遍歷方式和數組是一致的,支持索引遍歷和for range遍歷。
package main
import (
"fmt"
)
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7}
for i := 0; i < len(slice); i++ {
fmt.Printf("[%v-%v]\t", i, slice[i])
}
fmt.Println("")
for k, v := range slice {
fmt.Printf("[%v-%v]\t", k, v)
}
}
運行結果
[0-0] [1-1] [2-2] [3-3] [4-4] [5-5] [6-6] [7-7]
[0-0] [1-1] [2-2] [3-3] [4-4] [5-5] [6-6] [7-7]
2、切片的復制拷貝
- 直接賦值拷貝,淺拷貝
拷貝前后兩個變量共享底層數組,對一個切片的修改會影響另一個切片的內容
package main
import (
"fmt"
)
func main() {
slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
slice2 := slice1
slice2[0] = 100
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
運行結果
slice1: [100 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4 5 6 7]
- 使用
copy()
函數操作,深拷貝
copy(destSlice, srcSlice)
其中:destSlice-目標切片
???????????srcSlice-源切片
package main
import (
"fmt"
)
func main() {
slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
slice2 := make([]int, 5, 5)
copy(slice2, slice1)
fmt.Println("slice2:", slice2)
slice2[0] = 100
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
運行結果
slice2: [0 1 2 3 4]
slice1: [0 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4]
3、向切片中添加元素append
Go語言的內建函數append()可以為切片動態添加元素。 可以一次添加一個元素,可以添加多個元素,也可以添加另一個切片中的元素(后面加…)
slice = append(slice, num1, num2)
slice = append(slice, slice...)
- append函數會改變slice所引用的數組的內容,從而影響到引用同一數組的其它slice。
- 但當slice中沒有剩余空間(即(cap-len) == 0)時,切片就會自動按照一定的策略進行“擴容”,此時將動態分配新的數組空間。返回的slice數組指針將指向這個空間,而原數組的內容將保持不變;其它引用此數組的slice則不受影響。
package main
import (
"fmt"
)
func main() {
arr := [5]int{0, 1, 2, 3, 4}
slice := arr[:3]
slice = append(slice, 100)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
fmt.Println("------------------")
slice = append(slice, 200)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
fmt.Println("------------------")
slice = append(slice, 300)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
}
運行結果
arr: [0 1 2 100 4]
slice: [0 1 2 100]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200 300]
4、從切片中刪除元素
Go語言中并沒有刪除切片元素的專用方法,我們可以使用切片本身的特性來刪除元素。
slice = append(slice[:index], slice[index+1:]...)
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
slice = append(slice[:3], slice[4:]...)
fmt.Println(slice)
}
運行結果
[1 2 3 5]
5、注意事項
- 切片不能進行比較,切片唯一合法的比較操作是和nil比較。
- 判斷切片是否是空的,要使用
len(slice) == 0
來判斷,不應該使用slice == nil
來判斷
package main
import "fmt"
func main() {
var s1 []int
s2 := []int{}
s3 := make([]int, 0, 0)
fmt.Printf("s1: len = %v, cap = %v, isNil:%v\n", len(s1), cap(s1), s1 == nil)
fmt.Printf("s2: len = %v, cap = %v, isNil:%v\n", len(s2), cap(s2), s2 == nil)
fmt.Printf("s3: len = %v, cap = %v, isNil:%v\n", len(s3), cap(s3), s3 == nil)
}
運行結果
s1: len = 0, cap = 0, isNil:true
s2: len = 0, cap = 0, isNil:false
s3: len = 0, cap = 0, isNil:false