golang中的切片的索引是左閉右開(kāi)的,切片不存儲(chǔ)數(shù)據(jù)鸦致,它是對(duì)數(shù)組的引用,其他切片可以跟它共享同一個(gè)底層的數(shù)組腻惠,所以切片的其實(shí)是數(shù)組的別名。
a[1:4] 表示切片中的三個(gè)元素a[1],a[2],a[3]
slice可以看作是一個(gè)包含指針(指向底層數(shù)組)欲虚、切片中元素的個(gè)數(shù)以及切片容量的一個(gè)struct集灌。指向相同源數(shù)組的切片指針指向相同,容量相同复哆,但是輸出元素不同欣喧,這與切片的長(zhǎng)度有關(guān)系。重新切片同一個(gè)切片指針只能向后修改梯找,則前面的元素將被刪除唆阿。
// 切片a包含5個(gè)元素[1, 2, 3,4,5]
a := []int{1, 2, 3, 4, 5}
// 重新切片之后元素變?yōu)閇2, 3, 4, 5],第一個(gè)元素已經(jīng)被刪除锈锤,不能訪問(wèn)到第一個(gè)元素
a = a[1:]
對(duì)于使用append函數(shù)進(jìn)行元素?cái)U(kuò)充時(shí)驯鳖,如果當(dāng)前的容量不足以存放需要增加的元素,則將生成一個(gè)新的滿足容量需求的切片久免,如果容量滿足要求浅辙,則繼續(xù)用之前的切片。新切片的生成規(guī)則是容量變?yōu)樵瓉?lái)的2倍阎姥。如果是nil切片记舆,則會(huì)變成2。
package main
import "fmt"
func main() {
a := []int{1, 2}
fmt.Printf("cap = %d, slice = [%v], addr = [%p]\n", cap(a), a, a)
a = append(a, 3)
fmt.Printf("cap = %d, slice = [%v], addr = [%p]\n", cap(a), a, a)
var b []int
fmt.Printf("cap = %d, slice = [%v], addr = [%p]\n", cap(b), b, b)
b = append(b, 0)
fmt.Printf("cap = %d, slice = [%v], addr = [%p]\n", cap(b), b, b)
}
Output:
cap = 2, slice = [[1 2]], addr = [0x10410020]
cap = 4, slice = [[1 2 3]], addr = [0x10410040]
cap = 0, slice = [[]], addr = [0x0]
cap = 2, slice = [[0]], addr = [0x10410060]
如果切片引用了一個(gè)包含整個(gè)文件數(shù)據(jù)的數(shù)組呼巴,那么如果你僅僅需要其中一部分?jǐn)?shù)據(jù)泽腮,但是整個(gè)文件數(shù)據(jù)仍然在內(nèi)存中御蒲,除非沒(méi)有切片引用它,才會(huì)被GC回收诊赊。
// 返回的新切片仍然引用原始的數(shù)組厚满,最好的辦法就是copy將需要的數(shù)據(jù)進(jìn)行復(fù)制,這樣就不會(huì)導(dǎo)致一大堆無(wú)用的數(shù)據(jù)留在內(nèi)存中豪筝。
var digitRegexp = regexp.MustCompile("[0-9]+")
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}
// 使用copy改進(jìn)如下
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}
// 使用append改進(jìn)如下
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
var c []byte
return append(c, b...)
}
如何避免gotchas