文章目錄
- 零、概述
- 一、切片基礎
- 1、切片的結構
- 2、切片的創建方式
- 3、切片的操作與擴容
- 二、切片的拷貝與共享內存
- 三、切片作為函數參數
Go語言的切片(slice)是一種動態數組,提供了靈活、高效的元素序列操作。它基于底層數組實現,通過長度和容量的分離設計,支持動態擴容和內存優化。
零、概述
切片 vs 數組
特性 | 切片(slice) | 數組(array) |
---|---|---|
類型 | 引用類型 | 值類型 |
長度 | 動態可變 | 固定不可變 |
傳遞方式 | 傳遞指針副本,修改影響原數據 | 傳遞值拷貝,修改不影響原數據 |
創建方式 | make([]T, len, cap) 或[]T{...} | [n]T{...} 或[...]T{...} |
內存占用 | 包含指針、長度、容量 | 僅包含元素 |
性能優化原理:
- 預分配容量:若已知元素數量,使用
make([]T, 0, capacity)
避免頻繁擴容。- 避免內存泄漏:切片保留對底層數組的引用,若僅需部分元素,建議
copy
到新切片后釋放原數組。- 大容量切片謹慎擴容:大切片擴容可能導致內存浪費,可通過
copy
手動控制擴容策略。
?
一、切片基礎
1、切片的結構
切片是一個引用類型,底層結構包含三個字段:
type slice struct {array unsafe.Pointer // 指向底層數組的指針len int // 當前切片的長度cap int // 底層數組的容量
}
- 長度(len):切片中實際存在的元素數量,可通過
len()
函數獲取。 - 容量(cap):底層數組的總長度,可通過
cap()
函數獲取。 - 指針(array):指向底層數組的起始位置,決定了切片的起始元素。
示例:創建切片
s := make([]int, 3, 5) // 創建長度為3、容量為5的切片
// 底層數組: [0, 0, 0, 0, 0]
// 切片結構: {array: 指向數組, len: 3, cap: 5}
?
2、切片的創建方式
a. 使用make
函數
s1 := make([]int, 3) // 長度=容量=3,初始值為0
s2 := make([]int, 3, 5) // 長度=3,容量=5
b. 使用切片字面量
s3 := []int{1, 2, 3} // 長度=容量=3,初始值為[1,2,3]
s4 := []int{3: 100} // 索引3的值為100,長度=容量=4,初始值為[0,0,0,100]
c. 從數組或切片派生
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[1:3] // 從數組創建切片,[2,3],長度=2,容量=4
s6 := s5[0:2] // 從切片創建切片,[2,3],長度=2,容量=4
?
3、切片的操作與擴容
a. 訪問元素
通過索引訪問,范圍為0
到len(s)-1
:
s := []int{1, 2, 3}
fmt.Println(s[0]) // 輸出: 1
?
b. 基于切片創建切片
通過指定[low:high:max]
創建新切片:
low
:起始索引(包含)。high
:結束索引(不包含),新切片長度為high - low
。max
:容量上限,新切片容量為max - low
(可選,默認等于原切片容量)。
s := []int{10, 20, 30, 40, 50}
s1 := s[1:3] // 長度=2,容量=4,元素=[20,30]
s2 := s[1:3:3] // 長度=2,容量=2,元素=[20,30]
?
c. nil切片與空切片
var nilSlice []int // nil切片,array=nil,len=0,cap=0
emptySlice := make([]int, 0) // 空切片,array指向空數組,len=0,cap=0
- nil切片:未初始化,
array
為nil
。 - 空切片:已初始化,但長度為0,
array
指向底層數組。
?
d. 動態擴容:append
函數
s := make([]int, 0, 2) // 容量=2
s = append(s, 1, 2) // 追加元素,容量足夠,直接添加
s = append(s, 3) // 容量不足,觸發擴容(通常翻倍)
擴容規則:
- 容量
<1000
時,通常翻倍。- 容量
≥1000
時,按1.25倍增長(具體實現可能隨版本變化)。
?
e、多維切片
切片的元素可以是另一個切片,形成多維結構:
matrix := [][]int{{1, 2, 3},{4, 5, 6},
}fmt.Println(matrix[0][1]) // 輸出: 2
注意:多維切片的每個內層切片是獨立的,可擁有不同長度。
?
二、切片的拷貝與共享內存
- 淺拷貝(共享底層數組)
s1 := []int{1, 2, 3}
s2 := s1[0:2] // s2與s1共享底層數組
s1[0] = 100 // 修改s1會影響s2
fmt.Println(s2[0]) // 輸出: 100
- 深拷貝(獨立內存)
使用copy
函數:
s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1) // 復制元素到新切片
s1[0] = 100 // 修改s1不影響s2
fmt.Println(s2[0]) // 輸出: 1
?
三、切片作為函數參數
切片作為參數傳遞時,傳遞的是切片的值拷貝(即array
、len
、cap
的副本),但由于array
是指針,函數內可修改原切片的元素:
func modify(s []int) {s[0] = 100 // 修改底層數組的元素
}func main() {s := []int{1, 2, 3}modify(s)fmt.Println(s[0]) // 輸出: 100
}
若需在函數內擴容并影響原切片,需返回新切片:
func appendElement(s []int) []int {return append(s, 4)
}s := []int{1, 2, 3}
s = appendElement(s) // 需接收返回值以更新原切片