您現在的位置是:首頁 > 攝影首頁攝影
golang2021資料格式(18)切片
切片儲存的格式有哪幾種
陣列的長度在定義之後無法再次修改;陣列是值型別,每次傳遞都將產生一份副本。顯然這種資料結構無法完全滿足開發者的真實需求。Go語言提供了陣列切片(slice)來彌補陣列的不足。
Slice(切片)代表變長的序列,序列中每個元素都有相同的型別。一個slice型別一般寫作[]T,其中T代表slice中元素的型別;slice的語法和陣列很像,只是沒有固定長度而已。
陣列和slice之間有著緊密的聯絡。一個slice是一個輕量級的資料結構,提供了訪問陣列子序列(或者全部)元素的功能,而且slice的底層確實引用一個數組物件。一個slice由三個部分構成:指標、長度和容量。指標指向第一個slice元素對應的底層陣列元素的地址,要注意的是slice的第一個元素並不一定就是陣列的第一個元素。
切片並不是陣列或陣列指標,它透過內部指標和相關屬性引陣列段,以實現變案。
slice並不是真正意義上的動態陣列,而是一個引用型別。slice總是指向一個底層array,slice的宣告也可以像array一樣,只是不需要長度。
在講解切片(slice)之前,大家思考一下陣列有什麼問題?
第一:陣列定義完,長度是固定的。
例如:
定義的num陣列長度是5,表示只能儲存5個整型數字,現在向陣列num中追加一個數字,這時會出錯。
第二:使用陣列作為函式引數進行傳遞時,如果實參為5個元素的整型陣列,那麼形參也必須5個元素的整型陣列,否則出錯。
針對以上兩個問題,可以使用切片來進行解決。
切片:切片與陣列相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大,所以可以將切片理解成“動態陣列”,但是,它不是陣列。
slice和陣列的區別:宣告陣列時,[ ]內寫明瞭陣列的長度或使用。。。自動計算長度,而宣告slice時,[ ]內沒有任何字元。經常使用的切片建立方法:
自動推導型別建立slice
s1 := [] int {1, 2, 3, 4} 建立 有 4 個元素的切片,分別為:1234
藉助make建立 slice,格式:make(切片型別,長度,容量)
s2 := make([]int, 5, 10) len(s2) = 5, cap(s2) = 10
make時,沒有指定容量,那麼 長度==容量
s3 := make([]int, 5) len(s3) = 5, cap(s3) = 5
func main() {
s1 := [] int {1, 2, 3, 4} // 建立 有4個元素的切片
fmt。Println(“s1=”, s1)
s2 := make([]int, 5, 10) // 藉助make建立 slice,格式:make(切片型別,長度,容量)
s2[4] = 7
//s2[5] = 9 // 報錯:panic: runtime error: index out of range
fmt。Println(“s2=”, s2)
fmt。Printf(“len(s2)=%d, cap(s2)=%d\n”, len(s2), cap(s2))
s3 := make([]int, 5) // make時,沒指定容量,那麼 長度 == 容量
s3[2] = 3
fmt。Println(“s3=”, s3)
fmt。Printf(“len(s2)=%d, cap(s2)=%d\n”, len(s3), cap(s3))
}
注意:make只能建立slice、map和channel,並且返回一個有初始值(非零)的物件。
1。 切片Slice
需要說明,slice 並不是陣列或陣列指標。它透過內部指標和相關屬性引用陣列片段,以實現變長方案。
1。 切片:切片是陣列的一個引用,因此切片是引用型別。但自身是結構體,值複製傳遞。
2。 切片的長度可以改變,因此,切片是一個可變的陣列。
3。 切片遍歷方式和陣列一樣,可以用len()求長度。表示可用元素數量,讀寫操作不能超過該限制。
4。 cap可以求出slice最大擴張容量,不能超出陣列限制。0 <= len(slice) <= len(array),其中array是slice引用的陣列。
5。 切片的定義:var 變數名 []型別,比如 var str []string var arr []int。
6。 如果 slice == nil,那麼 len、cap 結果都等於 0。
1。1。1。 建立切片的各種方式
package main
import “fmt”
func main() {
//1。宣告切片
var s1 []int
if s1 == nil {
fmt。Println(“是空”)
} else {
fmt。Println(“不是空”)
}
// 2。:=
s2 := []int{}
// 3。make()
var s3 []int = make([]int, 0)
fmt。Println(s1, s2, s3)
// 4。初始化賦值
var s4 []int = make([]int, 0, 0)
fmt。Println(s4)
s5 := []int{1, 2, 3}
fmt。Println(s5)
// 5。從陣列切片
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// 前包後不包
s6 = arr[1:4]
fmt。Println(s6)
}
1。1。2。 切片初始化
全域性:
var arr = [。。。]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最後一個元素
區域性:
arr2 := [。。。]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最後一個元素
程式碼:
package main
import (
“fmt”
)
var arr = [。。。]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] //可以簡寫為 var slice []int = arr[:end]
var slice2 []int = arr[5:10] //可以簡寫為 var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最後一個元素
func main() {
fmt。Printf(“全域性變數:arr %v\n”, arr)
fmt。Printf(“全域性變數:slice0 %v\n”, slice0)
fmt。Printf(“全域性變數:slice1 %v\n”, slice1)
fmt。Printf(“全域性變數:slice2 %v\n”, slice2)
fmt。Printf(“全域性變數:slice3 %v\n”, slice3)
fmt。Printf(“全域性變數:slice4 %v\n”, slice4)
fmt。Printf(“——————————————————-\n”)
arr2 := [。。。]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] //可以簡寫為 slice := arr[:end]
slice7 := arr[5:10] //可以簡寫為 slice := arr[start:]
slice8 := arr[0:len(arr)] //slice := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最後一個元素
fmt。Printf(“區域性變數: arr2 %v\n”, arr2)
fmt。Printf(“區域性變數: slice5 %v\n”, slice5)
fmt。Printf(“區域性變數: slice6 %v\n”, slice6)
fmt。Printf(“區域性變數: slice7 %v\n”, slice7)
fmt。Printf(“區域性變數: slice8 %v\n”, slice8)
fmt。Printf(“區域性變數: slice9 %v\n”, slice9)
}
輸出結果:
全域性變數:arr [0 1 2 3 4 5 6 7 8 9]
全域性變數:slice0 [2 3 4 5 6 7]
全域性變數:slice1 [0 1 2 3 4 5]
全域性變數:slice2 [5 6 7 8 9]
全域性變數:slice3 [0 1 2 3 4 5 6 7 8 9]
全域性變數:slice4 [0 1 2 3 4 5 6 7 8]
——————————————————-
區域性變數: arr2 [9 8 7 6 5 4 3 2 1 0]
區域性變數: slice5 [2 3 4 5 6 7]
區域性變數: slice6 [0 1 2 3 4 5]
區域性變數: slice7 [5 6 7 8 9]
區域性變數: slice8 [0 1 2 3 4 5 6 7 8 9]
區域性變數: slice9 [0 1 2 3 4 5 6 7 8]
1。1。3。 透過make來建立切片
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
程式碼:
package main
import (
“fmt”
)
var slice0 []int = make([]int, 10)
var slice1 = make([]int, 10)
var slice2 = make([]int, 10, 10)
func main() {
fmt。Printf(“make全域性slice0 :%v\n”, slice0)
fmt。Printf(“make全域性slice1 :%v\n”, slice1)
fmt。Printf(“make全域性slice2 :%v\n”, slice2)
fmt。Println(“————————————————————”)
slice3 := make([]int, 10)
slice4 := make([]int, 10)
slice5 := make([]int, 10, 10)
fmt。Printf(“make區域性slice3 :%v\n”, slice3)
fmt。Printf(“make區域性slice4 :%v\n”, slice4)
fmt。Printf(“make區域性slice5 :%v\n”, slice5)
}
輸出結果:
make全域性slice0 :[0 0 0 0 0 0 0 0 0 0]
make全域性slice1 :[0 0 0 0 0 0 0 0 0 0]
make全域性slice2 :[0 0 0 0 0 0 0 0 0 0]
————————————————————
make區域性slice3 :[0 0 0 0 0 0 0 0 0 0]
make區域性slice4 :[0 0 0 0 0 0 0 0 0 0]
make區域性slice5 :[0 0 0 0 0 0 0 0 0 0]
切片的記憶體佈局
讀寫操作實際目標是底層陣列,只需注意索引號的差別。
package main
import (
“fmt”
)
func main() {
data := [。。。]int{0, 1, 2, 3, 4, 5}
s := data[2:4]
s[0] += 100
s[1] += 200
fmt。Println(s)
fmt。Println(data)
}
輸出:
[102 203]
[0 1 102 203 4 5]
可直接建立 slice 物件,自動分配底層陣列。
package main
import “fmt”
func main() {
s1 := []int{0, 1, 2, 3, 8: 100} // 透過初始化表示式構造,可使用索引號。
fmt。Println(s1, len(s1), cap(s1))
s2 := make([]int, 6, 8) // 使用 make 建立,指定 len 和 cap 值。
fmt。Println(s2, len(s2), cap(s2))
s3 := make([]int, 6) // 省略 cap,相當於 cap = len。
fmt。Println(s3, len(s3), cap(s3))
}
輸出結果:
[0 1 2 3 0 0 0 0 100] 9 9
[0 0 0 0 0 0] 6 8
[0 0 0 0 0 0] 6 6
使用 make 動態建立slice,避免了陣列必須用常量做長度的麻煩。還可用指標直接訪問底層陣列,退化成普通陣列操作。
package main
import “fmt”
func main() {
s := []int{0, 1, 2, 3}
p := &s[2] // *int, 獲取底層陣列元素指標。
*p += 100
fmt。Println(s)
}
輸出結果:
[0 1 102 3]
至於 [][]T,是指元素型別為 []T 。
package main
import (
“fmt”
)
func main() {
data := [][]int{
[]int{1, 2, 3},
[]int{100, 200},
[]int{11, 22, 33, 44},
}
fmt。Println(data)
}
輸出結果:
[[1 2 3] [100 200] [11 22 33 44]]
可直接修改 struct array/slice 成員。
package main
import (
“fmt”
)
func main() {
d := [5]struct {
x int
}{}
s := d[:]
d[1]。x = 10
s[2]。x = 20
fmt。Println(d)
fmt。Printf(“%p, %p\n”, &d, &d[0])
}
輸出結果:
[{0} {10} {20} {0} {0}]
0xc4200160f0, 0xc4200160f0
1。1。4。 用append內建函式操作切片(切片追加)
package main
import (
“fmt”
)
func main() {
var a = []int{1, 2, 3}
fmt。Printf(“slice a : %v\n”, a)
var b = []int{4, 5, 6}
fmt。Printf(“slice b : %v\n”, b)
c := append(a, b。。。)
fmt。Printf(“slice c : %v\n”, c)
d := append(c, 7)
fmt。Printf(“slice d : %v\n”, d)
e := append(d, 8, 9, 10)
fmt。Printf(“slice e : %v\n”, e)
}
輸出結果:
slice a : [1 2 3]
slice b : [4 5 6]
slice c : [1 2 3 4 5 6]
slice d : [1 2 3 4 5 6 7]
slice e : [1 2 3 4 5 6 7 8 9 10]
append :向 slice 尾部新增資料,返回新的 slice 物件。
package main
import (
“fmt”
)
func main() {
s1 := make([]int, 0, 5)
fmt。Printf(“%p\n”, &s1)
s2 := append(s1, 1)
fmt。Printf(“%p\n”, &s2)
fmt。Println(s1, s2)
}
輸出結果:
0xc42000a060
0xc42000a080
[] [1]
1。1。5。 超出原 slice。cap 限制,就會重新分配底層陣列,即便原陣列並未填滿。
package main
import (
“fmt”
)
func main() {
data := [。。。]int{0, 1, 2, 3, 4, 10: 0}
s := data[:2:3]
s = append(s, 100, 200) // 一次 append 兩個值,超出 s。cap 限制。
fmt。Println(s, data) // 重新分配底層陣列,與原陣列無關。
fmt。Println(&s[0], &data[0]) // 比對底層陣列起始指標。
}
輸出結果:
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc4200160f0 0xc420070060
從輸出結果可以看出,append 後的 s 重新分配了底層陣列,並複製資料。如果只追加一個值,則不會超過 s。cap 限制,也就不會重新分配。 通常以 2 倍容量重新分配底層陣列。在大批次新增資料時,建議一次性分配足夠大的空間,以減少記憶體分配和資料複製開銷。或初始化足夠長的 len 屬性,改用索引號進行操作。及時釋放不再使用的 slice 物件,避免持有過期陣列,造成 GC 無法回收。
1。1。6。 slice中cap重新分配規律:
package main
import (
“fmt”
)
func main() {
s := make([]int, 0, 1)
c := cap(s)
for i := 0; i < 50; i++ {
s = append(s, i)
if n := cap(s); n > c {
fmt。Printf(“cap: %d -> %d\n”, c, n)
c = n
}
}
}
輸出結果:
cap: 1 -> 2
cap: 2 -> 4
cap: 4 -> 8
cap: 8 -> 16
cap: 16 -> 32
cap: 32 -> 64
1。1。7。 切片複製
package main
import (
“fmt”
)
func main() {
s1 := []int{1, 2, 3, 4, 5}
fmt。Printf(“slice s1 : %v\n”, s1)
s2 := make([]int, 10)
fmt。Printf(“slice s2 : %v\n”, s2)
copy(s2, s1)
fmt。Printf(“copied slice s1 : %v\n”, s1)
fmt。Printf(“copied slice s2 : %v\n”, s2)
s3 := []int{1, 2, 3}
fmt。Printf(“slice s3 : %v\n”, s3)
s3 = append(s3, s2。。。)
fmt。Printf(“appended slice s3 : %v\n”, s3)
s3 = append(s3, 4, 5, 6)
fmt。Printf(“last slice s3 : %v\n”, s3)
}
輸出結果:
slice s1 : [1 2 3 4 5]
slice s2 : [0 0 0 0 0 0 0 0 0 0]
copied slice s1 : [1 2 3 4 5]
copied slice s2 : [1 2 3 4 5 0 0 0 0 0]
slice s3 : [1 2 3]
appended slice s3 : [1 2 3 1 2 3 4 5 0 0 0 0 0]
last slice s3 : [1 2 3 1 2 3 4 5 0 0 0 0 0 4 5 6]
copy :函式 copy 在兩個 slice 間複製資料,複製長度以 len 小的為準。兩個 slice 可指向同一底層陣列,允許元素區間重疊。
package main
import (
“fmt”
)
func main() {
data := [。。。]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt。Println(“array data : ”, data)
s1 := data[8:]
s2 := data[:5]
fmt。Printf(“slice s1 : %v\n”, s1)
fmt。Printf(“slice s2 : %v\n”, s2)
copy(s2, s1)
fmt。Printf(“copied slice s1 : %v\n”, s1)
fmt。Printf(“copied slice s2 : %v\n”, s2)
fmt。Println(“last array data : ”, data)
}
輸出結果:
array data : [0 1 2 3 4 5 6 7 8 9]
slice s1 : [8 9]
slice s2 : [0 1 2 3 4]
copied slice s1 : [8 9]
copied slice s2 : [8 9 2 3 4]
last array data : [8 9 2 3 4 5 6 7 8 9]
應及時將所需資料 copy 到較小的 slice,以便釋放超大號底層陣列記憶體。
1。1。8。 slice遍歷:
package main
import (
“fmt”
)
func main() {
data := [。。。]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := data[:]
for index, value := range slice {
fmt。Printf(“inde : %v , value : %v\n”, index, value)
}
}
輸出結果:
inde : 0 , value : 0
inde : 1 , value : 1
inde : 2 , value : 2
inde : 3 , value : 3
inde : 4 , value : 4
inde : 5 , value : 5
inde : 6 , value : 6
inde : 7 , value : 7
inde : 8 , value : 8
inde : 9 , value : 9
1。1。9。 切片resize(調整大小)
package main
import (
“fmt”
)
func main() {
var a = []int{1, 3, 4, 5}
fmt。Printf(“slice a : %v , len(a) : %v\n”, a, len(a))
b := a[1:2]
fmt。Printf(“slice b : %v , len(b) : %v\n”, b, len(b))
c := b[0:3]
fmt。Printf(“slice c : %v , len(c) : %v\n”, c, len(c))
}
輸出結果:
slice a : [1 3 4 5] , len(a) : 4
slice b : [3] , len(b) : 1
slice c : [3 4 5] , len(c) : 3
1。1。10。 陣列和切片的記憶體佈局
1。1。11。 字串和切片(string and slice)
string底層就是一個byte的陣列,因此,也可以進行切片操作。
package main
import (
“fmt”
)
func main() {
str := “hello world”
s1 := str[0:5]
fmt。Println(s1)
s2 := str[6:]
fmt。Println(s2)
}
輸出結果:
hello
world
string本身是不可變的,因此要改變string中字元。需要如下操作: 英文字串:
package main
import (
“fmt”
)
func main() {
str := “Hello world”
s := []byte(str) //中文字元需要用[]rune(str)
s[6] = ‘G’
s = s[:8]
s = append(s, ‘!’)
str = string(s)
fmt。Println(str)
}
輸出結果:
Hello Go!
1。1。12。 含有中文字串:
package main
import (
“fmt”
)
func main() {
str := “你好,世界!hello world!”
s := []rune(str)
s[3] = ‘夠’
s[4] = ‘浪’
s[12] = ‘g’
s = s[:14]
str = string(s)
fmt。Println(str)
}
輸出結果:
你好,夠浪!hello go
golang slice data[:6:8] 兩個冒號的理解
常規slice , data[6:8],從第6位到第8位(返回6, 7),長度len為2, 最大可擴充長度cap為4(6-9)
另一種寫法: data[:6:8] 每個數字前都有個冒號, slice內容為data從0到第6位,長度len為6,最大擴充項cap設定為8
a[x:y:z] 切片內容 [x:y] 切片長度: y-x 切片容量:z-x
package main
import (
“fmt”
)
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
d1 := slice[6:8]
fmt。Println(d1, len(d1), cap(d1))
d2 := slice[:6:8]
fmt。Println(d2, len(d2), cap(d2))
}
陣列or切片轉字串:
strings。Replace(strings。Trim(fmt。Sprint(array_or_slice), “[]”), “ ”, “,”, -1)