切片
什麼是切片
- 切片長度不固定,不設定長度
- 內建資料型別
- 三元素:
- 指標 slice 指定的開始位置
- 長度 slice 的長度
- 容量 slice 開始到最後的最大長度
package main
import "fmt"
func main() {
/*
切片特點:
- 切片長度不固定,不設定長度
- 內建資料型別
- 三元素:
- 指標 slice 指定的開始位置
- 長度 slice 的長度
- 容量 slice 開始到最後的最大長度
*/
// 定義切片
slice01 := []int{} //
fmt.Printf("%T\n", slice01)
fmt.Println("預設切片:", slice01)
fmt.Println("切片長度:", len(slice01))
fmt.Println("切片容量:", cap(slice01))
// 定義空切片 slice02 == nil
var slice02 []int
if slice02 == nil {
fmt.Println(slice02 == nil)
fmt.Println("切片空", slice02)
}
// 初始化切片
slice03 := []int{1, 2, 3, 4}
fmt.Println("slice03有值切片:", slice03)
fmt.Printf("%T\n", slice03)
fmt.Println("slice03切片長度:", len(slice03))
fmt.Println("slice03切片容量:", cap(slice03))
}
make 定義切片
package main
import "fmt"
func main() {
// make 函式 ----> 建立切片
slice004 := make([]int, 5, 10)
fmt.Printf("slice004切片資料:%T\n", slice004)
fmt.Printf("slice004切片長度:%d\n", len(slice004))
fmt.Printf("slice004切片容量:%d\n", cap(slice004))
// 容量為5 能放6個資料嗎? 【不能,超出容量】
//slice004[6] = 1
//fmt.Println(slice004) // index out of range [6] with length 5
// 切片如何擴容. append
slice004 = append(slice004, 7, 1)
fmt.Println(slice004)
// 容量也會擴容. 遵循二倍擴容:1.當前容量兩擴大1倍數.
slice004 = append(slice004, 7, 1, 2, 37, 1, 2, 37, 1, 2, 37, 1, 2, 37, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6)
slice004 = append(slice004, 7, 1, 2, 37, 1, 2, 37, 1, 2, 37, 1, 2, 37, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6)
fmt.Println("擴容後的slice004:", slice004)
fmt.Printf("擴容後slice004切片資料:%T\n", slice004)
fmt.Printf("擴容後slice004切片長度:%d\n", len(slice004))
fmt.Printf("擴容後slice004切片容量:%d\n", cap(slice004))
}
切片追加
package main
import "fmt"
func main() {
// 切片追加: ...切片 表示拆解切片內的元素
slice05 := make([]int, 0, 3)
slice06 := []int{1, 2, 3, 4, 5, 6}
// 將 slice06 追加到 slice05 中
// slice06... 。即: 將slice06 進行了結構操作,迴圈 slice06中的每一個元素
slice05 = append(slice05, slice06...)
fmt.Println("slice05:", slice05)
}
遍歷切片
package main
import "fmt"
func main() {
// 遍歷切片
ergodicSlice1 := []int{1, 2, 3, 4}
for i := 0; i < len(ergodicSlice1); i++ {
fmt.Println(ergodicSlice1[i])
}
ergodicSlice2 := []int{1, 2, 3, 4}
for index, sliceVal := range ergodicSlice2 {
fmt.Println(index, sliceVal)
}
}
切片記憶體分析
- 切片 容量擴充/擴容記憶體 分析
- cap 成倍增加。容量超過1024,則按照1.2倍數增長
- 容量增加,記憶體地址就會發生變化
package main
import "fmt"
func main() {
// 切片 容量擴充/擴容記憶體 分析
// cap 成倍增加。容量超過1024,則按照1.2倍數增長
// 容量增加,記憶體地址就會發生變化
sliceMemory := []int{1, 2, 3}
fmt.Printf("切片長度:%d 切片容量:%d\n", len(sliceMemory), cap(sliceMemory))
fmt.Printf("切片地址:%p\n", sliceMemory)
sliceMemory = append(sliceMemory, 1, 2)
fmt.Printf("切片長度:%d 切片容量:%d\n", len(sliceMemory), cap(sliceMemory))
fmt.Printf("切片地址:%p\n", sliceMemory)
sliceMemory = append(sliceMemory, 2, 3)
fmt.Printf("切片長度:%d 切片容量:%d\n", len(sliceMemory), cap(sliceMemory))
fmt.Printf("切片地址:%p\n", sliceMemory)
/*
總結:
- 1. 每一個切片,底層是陣列
- 2. 切片本身不儲存資料,而是透過底層陣列儲存資料。修改切片的容量,也就修改陣列中的資料,記憶體地址發生變化
- 3. append 追加資料/切片擴容時。只要容量夠用,指向同一個【記憶體地址的陣列】
- 4. 陣列屬於值型別copy
*/
}
切片擴容
package main
import "fmt"
func main() {
// 透過使用 copy 函式 實現切片make擴容,copy實現原資料到新切片
sliceCopyBefore := []int{1, 2, 3}
fmt.Printf("原切片。長度:%d, 容量:%d\n", len(sliceCopyBefore), cap(sliceCopyBefore))
fmt.Printf("原切片記憶體地址:%p\n", sliceCopyBefore)
// 1. make 擴容 新切片
sliceCopyAfter := make([]int, cap(sliceCopyBefore)*2)
// 2. 透過 copy 將 原切片內的陣列中資料複製到新的切片陣列中的資料
copy(sliceCopyAfter, sliceCopyBefore)
fmt.Printf("新切片。長度:%d, 容量:%d\n", len(sliceCopyAfter), cap(sliceCopyAfter))
fmt.Printf("新切片記憶體地址:%p\n", sliceCopyAfter)
}
陣列的切片
package main
import "fmt"
func main() {
// 陣列:連續的記憶體地址
// 切片底層指向一個陣列
arraySlice := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
arraySlice01 := arraySlice[0:3] // 指向同一個地址 。 記憶體地址:0xc000026140
arraySlice02 := arraySlice[:5] // 指向同一個地址 。 記憶體地址:0xc000026140
arraySlice03 := arraySlice[2:5] // 記憶體地址發生變化
arraySlice04 := arraySlice[6:] // 記憶體地址發生變化
arraySlice05 := arraySlice[:10] // 指向同一個地址 。 記憶體地址:0xc000026140
/*
結果:
- 1. 使用 [start:end] 對陣列進行切片
- 2. 起始位置從頭開始,則使用同一塊陣列記憶體地址。
- 3. 從中間位置開始 切。 容量會切到最後。eg [2:5],容量為8,長度為3
- 4. 陣列, 使用 [start:end] 結果會變成 切片型別
- 5. 容量不變。修改陣列和切片。指向同一個資料。
- 6. 容量擴容。切片地址發生記憶體變化
*/
fmt.Printf("arraySlice01 陣列切片後。長度:%d,容量:%d, 型別:%T, 記憶體地址:%p\n", len(arraySlice01), cap(arraySlice01), arraySlice01, arraySlice01)
fmt.Printf("arraySlice02 陣列切片後。長度:%d,容量:%d, 型別:%T, 記憶體地址:%p\n", len(arraySlice02), cap(arraySlice02), arraySlice02, arraySlice02)
fmt.Printf("arraySlice03 陣列切片後。長度:%d,容量:%d, 型別:%T, 記憶體地址:%p\n", len(arraySlice03), cap(arraySlice03), arraySlice03, arraySlice03)
fmt.Printf("arraySlice04 陣列切片後。長度:%d,容量:%d, 型別:%T, 記憶體地址:%p\n", len(arraySlice04), cap(arraySlice04), arraySlice04, arraySlice04)
fmt.Printf("arraySlice04 最新截斷的第一個元素的記憶體地址:%p\n", arraySlice04)
fmt.Printf("arraySlice 第6個元素的記憶體地址:%p\n", &arraySlice[6])
fmt.Printf("arraySlice05 陣列切片後。長度:%d,容量:%d, 型別:%T, 記憶體地址:%p\n", len(arraySlice05), cap(arraySlice05), arraySlice05, arraySlice05)
fmt.Printf("arraySlice05 陣列第一個元素 記憶體地址:%p\n", arraySlice05)
}
切片的型別
package main
import "fmt"
func main() {
// 陣列 是值型別
array1 := [3]int{1, 2, 3}
array2 := array1
array1[2] = 100
// 深複製。指向不同的記憶體地址
fmt.Printf("array1:%v,array2:%v\n", array1, array2) // array1:[1 2 100],array2:[1 2 3]
// 切片 是引用型別
// 淺複製,指向同一個記憶體地址
s1 := []int{1, 2, 3}
s2 := s1
s2[1] = 200
fmt.Printf("s1:%v,s2:%v\n", s1, s2) // s1:[1 200 3],s2:[1 200 3]
}
切片如何實現深複製
- 使用make開闢新的切片
- 使用copy函式實現 切片深複製
package main
import "fmt"
func main() {
/*
// 切片如何實現 深複製
- 1. 使用make開闢新的切片
- 2. 使用copy函式實現 切片深複製
*/
s3 := []int{1, 2, 3, 4, 5}
s4 := make([]int, 0)
for _, i := range s3 {
s4 = append(s4, i)
}
// 修改s4
s4[3] = 100
fmt.Printf("s3:%p,s4:%p\n", s3, s4) // s3:0xc000024210,s4:0xc0000221c0
fmt.Printf("s3:%v,s4:%v\n", s3, s4) // s3:[1 2 3 4 5],s4:[1 2 3 100 5]
// 空型別 切片複製:copy 不會以追加方式複製,而是以相同位置替換。
// s3中的函式 複製到 s5 : 【 dst, src 】
s5 := []int{1, 1, 1, 1, 1, 1, 1, 1, 1}
copy(s5, s3)
fmt.Printf("s3:%p,s5:%p\n", s3, s5) // s3:0xc0000b8000,s5:0x1d97300
fmt.Printf("s3:%v,s5:%v\n", s3, s5)
// s6中的函式 複製到 s3 : 【 dst, src 】
// 空型別 切片複製:copy 不會以追加方式複製,而是以相同位置替換。
s6 := []int{1, 1, 1, 1, 1, 1, 1, 1, 1}
copy(s3, s6)
fmt.Printf("s3:%p,s6:%p\n", s3, s6) // s3:0xc000024210,s6:0xc000026140
fmt.Printf("s3:%v,s6:%v\n", s3, s6) // s3:[1 1 1 1 1],s6:[1 1 1 1 1 1 1 1 1]
}
陣列和切片作為函式的引數
package main
import "fmt"
func updateSlice(s []int) {
fmt.Println("函式接收切片的值:", s)
s[2] = 100
fmt.Println("函式修改切片的值:", s)
}
func updateArray(a [3]int) {
fmt.Println("函式接收陣列的值:", a)
a[2] = 100
fmt.Println("函式修改陣列的值:", a)
}
func main() {
a1 := [3]int{1, 2, 3}
s1 := []int{1, 2, 3}
fmt.Println("切片傳入函式前的值:", s1)
updateSlice(s1)
fmt.Println("切片傳入函式後的值:", s1)
fmt.Println("-----------")
fmt.Println("陣列傳入函式前的值:", a1)
updateArray(a1)
fmt.Println("陣列傳入函式後的值:", a1)
}