day05 切片slice

染指未来發表於2024-07-02

切片

什麼是切片

  • 切片長度不固定,不設定長度
  • 內建資料型別
  • 三元素:
    • 指標 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 定義切片

  • 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)

}

相關文章