Go語言切片前或中間插入項與內置copy()函數詳解
內置append()函數能夠在切片末尾位置添加新的項,假設要在切片的前面或者中間某位置插入特定項,可以這樣實現
看下代碼:
package main import "fmt" func main() { s := []string{"M","N","O","P","Q","R"} x := InsertStringSliceCopy(s,[]string{"a","b","c"},0) y := InsertStringSliceCopy(s,[]string{"a","b","c"},3) fmt.Printf("%v\n%v\n",x,y) } func InsertStringSliceCopy(slice,insertion []string,index int)[]string { result := make([]string,len(slice) + len(insertion)) at := copy(result,slice[:index]) at += copy(result[at:],insertion) copy(result[at:],slice[index:]) fmt.Printf("%6T\n",at) return result }
運行結果:
自定義的InsertStringSliceCopy()函數可以實現在切片相應的位置插入項
此外InsertStringSliceCopy()函數中打印類變量at的類型,可知內置函數copy()在實現復制功能的時候會有一個int的返回值
補充:go學習備忘錄 – 切片中間插入元素
1. 通過鏈式append 實現
將多個append操作組合起來,實現在切片中間插入元素:
var a []int a = append(a[:i], append([]int{1}, a[i:]...)...) // 在第i個位置插入1 a = append(a[:i], append([]int{1,2,3}, a[i:]...)...) // 在第i個位置插入切片
每個鏈式操作中的第二個append調用都會創建一個臨時切片,並將a[i:]的內容復制到新創建的切片中,然後將臨時創建的切片再追加到a[:i]。
2. 通過copy + append 實現
通過 copy和append組合 可以避免創建中間的臨時切片
a = append(a, 0) // 切片擴展1個空間 copy(a[i+1:], a[i:]) // a[i:]向後移動1個位置 a[i] = x // 設置新添加的元素
用copy和append組合在中間位置插入多個元素(也就是插入一個切片):
a = append(a, x...) // 為x切片擴展足夠的空間 copy(a[i+len(x):], a[i:]) // a[i:]向後移動len(x)個位置 copy(a[i:], x) // 復制新添加的切片
註:append本質是用於追加元素而不是擴展容量,擴展切片容量隻是append的一個副作用。
補充:Go語言中切片作為函數參數,函數中使用append添加元素
切片作為函數,通過append添加元素,有可能會更改地址:
1)添加的數據元素長度超過切片參數的容量,則會另開辟空間,重新分配底層數組,並復制數據。函數中的此切片與原切片地址不同; 此切片指向新開辟的內存。函數運行結束,內存釋放,不會影響元切片的內容。
2)否則原切片與函數中的切片指向同一地址。會影響切片的內容。
3)切片名本身就是一個指針(內容保存指向切片的首地址)
代碼測試:
package main import "fmt" func main01() { s := make([]int, 3, 5) s[2] = 8888 fmt.Printf("原地址:%p", s) s = append(s, 12) fmt.Printf("\n添加數據之後的地址:%p", s) /* append添加元素,容量足夠,則在原基礎之上添加數據,地址不會發生改變 輸出: 原地址:0xc04207e030 添加數據之後的地址:0xc04207e030 */ } func main02() { s := make([]int, 3) s[2] = 666 fmt.Printf("append添加數據之前的地址:%p", s) s = append(s, 888) fmt.Printf("\nappend添加數據之後的地址:%p", s) /* append添加數據,容量不夠,則另行開辟空間,切片地址發生變化 輸出: append添加數據之前的地址:0xc04200e2c0 append添加數據之後的地址:0xc04200a2d0 */ } func main() { /* copy(目的切片,原切片):切片拷貝 註意事項:目的切片要有足夠的空間,如果沒有空間(切片為空或者指向0x0),不能進行拷貝 若目的切片容量不足,隻拷貝部分(目的切片長度的部分) 返回值為拷貝成功的切片數量 */ s := make([]int, 3) s[0] = 0 s[1] = 111 s[2] = 666 //var s1 []int = []int{5: 333} //n:=copy(s,s1) s1 := make([]int, 1, 2) n := copy(s1, s) fmt.Printf("原切片s的地址是:%p", s) fmt.Printf("\n拷貝之後的切片s1的地址是:%p,數量:%d", s1, n) fmt.Println(s1) }
補充說明:
數組和slice之間有著緊密的聯系。一個slice是一個輕量級的數據結構,提供瞭訪問數組子序列(或者全部)元素的功能,而且slice的底層確實引用一個數組對象。一個slice由三個部分構成:指針、長度和容量。指針指向第一個slice元素對應的底層數組元素的地址,要註意的是slice的第一個元素並不一定就是數組的第一個元素。
切片並不是數組或數組指針,它通過內部指針和相關屬性引⽤數組⽚段,以實現變⻓⽅案。
slice並不是真正意義上的動態數組,而是一個引用類型。slice總是指向一個底層array,slice的聲明也可以像array一樣,隻是不需要長度。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。