GO 切片实力踩坑
概述
GO 語言的切片這兩天用了用, 可以支持切割數(shù)組的中間部分. 但今天使用中, 出了 bug, 查了半天, 發(fā)現(xiàn)是切片的問題, 簡單寫個 demo 復(fù)現(xiàn)當(dāng)時的情況:
package mainimport "fmt"func main() {a := []int{1, 2, 3, 4, 5}b := a[2:4]b[0] = 9fmt.Println(a) }你以為輸出的是什么? 來, 看結(jié)果:
[1 2 9 4 5]
懵沒懵?? 這是怎么回事呢?
(我用個語言怎么老踩坑, 笨的一X)
解惑
看這段 GO 代碼的輸出, 我們在修改b數(shù)組第一個元素值的時候, a數(shù)組的第三個元素修改了, 這兩個有什么聯(lián)系嗎? 仔細(xì)看, b數(shù)組在切的時候, 切的不就是a數(shù)組第三第四的元素嗎? 如此看來, b[0] 不正對應(yīng) a[2] 嗎?
大膽假設(shè): **GO 中對數(shù)組進(jìn)行切割, 并不會切一個新的數(shù)組出來, 而是仍然使用原數(shù)組, 只是修改下數(shù)組的首地址和長度. **
驗證:
package mainimport "fmt"func main() {a := []int{1, 2, 3, 4, 5}b := a[2:4]b[0] = 9fmt.Printf("%p\n", &a[2])fmt.Printf("%p\n", &b[0]) }打印出來的地址完全一致, 印證了之前的猜想, 果然是一個數(shù)組. 同時修改a數(shù)組, 也會影響到b數(shù)組.
那可不可以對b數(shù)組越界訪問, 訪問a數(shù)組的值呢? 不行, GO 會對數(shù)組進(jìn)行越界檢查.
查看文檔后發(fā)現(xiàn), GO 切片的內(nèi)部實現(xiàn)是這樣的包含了三個字段. 其中各字段含義如下:
如此看來, 對其進(jìn)行切割, 并不會整個復(fù)制, 對于大切片的操作就顯得很友好了, 畢竟共享底層數(shù)組, 只需要創(chuàng)建很少的數(shù)據(jù)就可以了. 只是要注意數(shù)組的同步修改問題.
這么看來, 貌似也可以解釋為什么叫切片了. **切片就是將底層的數(shù)組切出一部分來, 而不會創(chuàng)建新的數(shù)組. **
切片是有容量的, 那上面的切片b的容量是多少呢? 我看了一下: 長度是2, 容量是3.
GO 的切片在容量足夠的時候, 是不會動態(tài)擴(kuò)容的. (擴(kuò)容會創(chuàng)建更大容量新的數(shù)組并復(fù)制原數(shù)組數(shù)據(jù)). 那也就是說, 如果我向b追加數(shù)據(jù), 就可以影響到原數(shù)組的后面的數(shù)據(jù)了??
試一發(fā):
package mainimport "fmt"func main() {a := []int{1, 2, 3, 4, 5}b := a[2:4]b = append(b, 10)fmt.Println(b)fmt.Println(a) }果然, 容量允許的話, 追加操作使用的仍然是原始數(shù)組.
所以: 切片的容量其實是底層數(shù)組的容量
同時, 有了之前對 GO 的了解, 知道 GO 所有的函數(shù)都是以傳遞副本值的方式進(jìn)行, 傳遞切片也一樣, 而切片的結(jié)構(gòu)體包含(數(shù)組指針, 長度, 容量)三個元素, 底層數(shù)組并不屬于值本身, 所以切片在函數(shù)間傳遞的復(fù)制成本很小, 而且函數(shù)對切片的修改也會反應(yīng)到底層數(shù)組上. 同理可得, 如果在函數(shù)中對切片執(zhí)行了擴(kuò)容操作, 那改動就不會影響原數(shù)據(jù), 因為擴(kuò)容后操作的是新的數(shù)組了.
OK. 切片到這里就結(jié)束了, 簡單說就是數(shù)組上面再套一層. 切片的切片共享底層數(shù)組.
最后說一句, GO 創(chuàng)建數(shù)組和切片的方式(數(shù)組和切片是不同的數(shù)據(jù)結(jié)構(gòu)):
// 方括號為空, 創(chuàng)建的是切片類型 a := []int{1, 2} // 方括號指定長度, 創(chuàng)建的是真正的數(shù)組類型 b := [2]int{}總結(jié)
至此, 對 GO 的切片有了全新的認(rèn)識. 在使用切片的時候, 需要特別注意, 切片的截取與原對象共享底層數(shù)組, 在數(shù)據(jù)修改時要特別注意.
如果需要一個安全的可修改的切皮, 可以使用copy函數(shù)復(fù)制一個全新的數(shù)組出來, 與原數(shù)組分離就可以了.
總結(jié)
- 上一篇: MySQL 数据恢复
- 下一篇: java 继承作用_java三大特性之继