array 和slice都是數(shù)組,前者固定大小,值類型;后者可以動(dòng)態(tài)變更万牺,引用類型。
再次強(qiáng)調(diào)一遍糊秆,array在golang中是值類型,而不是c中的指針议双,在函數(shù)傳參時(shí)痘番,會(huì)重新復(fù)制整個(gè)數(shù)組。因而大多數(shù)都是使用slice.slice 底層是一個(gè)array平痰。兩者數(shù)據(jù)結(jié)構(gòu)如下:
初始化
array
var b [2]int // 零值
b := [2]int{1,2}
b := [...]int{1,2} // 自動(dòng)計(jì)算長(zhǎng)度slice
var s []int // 空指針,相當(dāng)于 ([]int)(nil)
s := []int{1,2} 或者通過(guò)make
s := make([]int,2) // 長(zhǎng)度和容量都是2
s := make([]int, 2, 4) //長(zhǎng)度為2汞舱,容量為4
s := b[:] // 從其他array切分,共享底層數(shù)據(jù),即修改其中一個(gè)觉增,另一個(gè)也會(huì)跟著改變
package danmu
import (
"fmt"
"testing"
)
func TestInterfaceKey(t *testing.T) {
var a [2]int
fmt.Println("array init", a[0])
var s []int
// s[0] 會(huì)直接報(bào)空指針錯(cuò)誤
s = a[:]
fmt.Println("create slice from a ", a, " \t", s)
a[0] = 1 // 修改array
s[1] = 2 // 修改slice
fmt.Println("after changed both value", a, " \t ", s)
}
bash-3.2$ go test -v -run TestInterfaceKey
=== RUN TestInterfaceKey
array init 0
create slice from a [0 0] [0 0]
after changed both value [1 2] [1 2]
slice 操作
先說(shuō)說(shuō)make函數(shù)兵拢。
func make([]T, len, cap) []T
len:數(shù)據(jù)大小;cap:容量大小翻斟,slice實(shí)際能訪問(wèn)的大小由len決定逾礁,有段時(shí)間覺(jué)得cap只是個(gè)占坑的。下面寫append時(shí)會(huì)說(shuō)到用處访惜。
-
切分操作
切分會(huì)生成一個(gè)共享源數(shù)據(jù)的slice嘹履。s表示數(shù)據(jù)源,可以是array也可以是slice; idx表示索引
- 整個(gè)拷貝 : s1 := s[:]
- 從某個(gè)開(kāi)始到末尾: s1 := s[idx:]
- 截到某個(gè)為止: s1 := s[:idx]
- 從某個(gè)開(kāi)始截取到某個(gè)為止: s1 := s[idx1:idx2]
追加操作
func append(slice [][Type], elems ...[Type]
,將數(shù)據(jù)添加到尾端债热,當(dāng)容量不足時(shí)砾嫉,會(huì)重新生成一個(gè)底層array,并把之前的數(shù)據(jù)復(fù)制過(guò)去窒篱。上面說(shuō)的cap焕刮,就是這個(gè)作用舶沿,事先分配合適的容量,可以提升性能配并。
用法:
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
package danmu
import (
"fmt"
"testing"
)
func TestInterfaceKey(t *testing.T) {
s := make([]int, 1, 3)
fmt.Printf("%p %v \n", s, s[0])
// fmt.Println(s[1]) 越界
s = append(s, 2)
fmt.Printf("%p %v \n", s, s[1]) // 注意這里的指針和上面的指針一樣括荡,因?yàn)闆](méi)有超出他的容量
s = append(s, 3, 4)
fmt.Printf("%p \n", s) // 這里指針變化。前面追加的時(shí)候溉旋,已經(jīng)超過(guò)了容量畸冲,會(huì)重新生成一個(gè)新的slice
s1 := s[1:]
fmt.Println(s1)
s1[0] = 1123
fmt.Println("after changed :: ", s1, " \t s0 : ", s)
}
-
混合使用
-
任意位置插入任意數(shù)據(jù)
s1 := make([]T, len(s[:idx]) copy(s1, s[:idx]) s = append(append(s1, value ),s[idx:]...)) 如果value是一個(gè)slice, append(s1, value )替換為append(s1, value... )
思考下為什么不直接用append([s[:idx],value)?
刪除數(shù)據(jù): s = append(s[:idx], [idx+N:])
-
為什么不直接用append(s[:idx],value)
切分生成的slice會(huì)共享源數(shù)據(jù),再調(diào)用append時(shí)观腊,如果這時(shí)容量足夠邑闲,就不會(huì)重新生成一個(gè)底層的array,相當(dāng)于修改之前的數(shù)據(jù),那么再次調(diào)用s[idx:]時(shí)梧油,相當(dāng)于在修改過(guò)的數(shù)據(jù)重新截取數(shù)據(jù)苫耸,會(huì)出現(xiàn)數(shù)據(jù)污染。
package danmu
import (
"fmt"
"testing"
)
func TestInterfaceKey(t *testing.T) {
s := make([]int, 3, 5) //下面追加another是不會(huì)超出容量
s[0] = 1
s[1] = 2
s[2] = 3
another := []int{200, 201}
s2 := append(append(s[:1], another...), s[1:]...)
fmt.Println(s2)
}
我們期望的結(jié)果:
s2 : [1,200,201,2,3]
實(shí)際運(yùn)行結(jié)果:
bash-3.2$ go test -v -run TestInterfaceKey
=== RUN TestInterfaceKey
[1 200 201 200 201]