1. Array
- 數(shù)組是值類(lèi)型,賦值和傳參都會(huì)拷貝整個(gè)數(shù)組;
- 數(shù)組長(zhǎng)度必須是常量,且是類(lèi)型的組成部分,
[2]int和[3]int是不同類(lèi)型; - 支持
==和!=操作符; - 指針數(shù)組
[10]*int,數(shù)組指針*[10]int; -
len和cap都返回?cái)?shù)組長(zhǎng)度; - 可用復(fù)合語(yǔ)句初始化;
a := [3]int{1, 2} // 未初始化的元素值為0
b := [...]int{1, 2, 3, 4} // 通過(guò)初始化值確定長(zhǎng)度
c := [5]int{2: 100, 3: 10} //使用索引號(hào)初始化元素
d := [...]struct {
name string
age int
}{
{"Jim", 10},
{"Jack", 11}, // 別忘了最后一行的逗號(hào)
}
e := [2][3]int{{1, 2, 3}, {4, 5, 6}}
f := [...][3]int{{1, 2, 3}, {4, 5, 6}} // 第二維不可用...
值拷貝會(huì)造成性能問(wèn)題,通常建議用slice或者數(shù)組指針。
2. slice
slice并不是數(shù)組或者數(shù)組指針。它通過(guò)內(nèi)部指針和相關(guān)屬性引用數(shù)組片段,以實(shí)現(xiàn)變長(zhǎng)方案。
- 引用類(lèi)型,自身是結(jié)構(gòu)體,值拷貝傳遞;
type slice struct {
array unsafe.Pointer
len int
cap int
}
- 屬性
len表示可用元素?cái)?shù)量,讀寫(xiě)操作不能夠超過(guò)該限制; - 屬性
cap表示最大容量,不能超過(guò)數(shù)組限制; - 如果
slice == nil,那么len和cap都為0;
創(chuàng)建slice時(shí),索引不能超過(guò)數(shù)組限制,讀寫(xiě)slice操作的是底層數(shù)組。
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
// s := a[1:9:9] // invalid slice index 9 索引不能超過(guò)數(shù)組限制
s := a[1:4:5]
s[2] = 10
fmt.Println(a, s) // [1 2 3 10 5 6 7 8] [2 3 10]
- 可直接創(chuàng)建
slice對(duì)象,自動(dòng)分配底層數(shù)組。
a := []int{1, 2, 3, 4, 8: 9} // 初始化創(chuàng)建,可使用索引號(hào)
fmt.Println(a, len(a), cap(a)) // [1 2 3 4 0 0 0 0 9] 9 9
b := make([]int, 6) // make創(chuàng)建,省略cap,相當(dāng)于len = cap
fmt.Println(b, len(b), cap(b)) // [0 0 0 0 0 0] 6 6
c := make([]int, 6, 8)
fmt.Println(c, len(c), cap(c)) // [0 0 0 0 0 0] 6 8
-
reslice是基于已有slice創(chuàng)建新slice對(duì)象
2.1. append
向slice尾部添加數(shù)據(jù),返回新的slice對(duì)象。一旦slice超出cap限制,就會(huì)重新分配底層數(shù)組,即便原數(shù)組并未填滿(mǎn)。
data := []int{1, 2, 3, 4, 8: 9}
s := data[0:2:3]
fmt.Println(&s[0], &data[0]) // 0xc000010280 0xc000010280
s = append(s, 100, 200) // 一次append兩個(gè)值,超出s.cap限制
fmt.Println(&s[0], &data[0]) // 0xc00000a2d0 0xc000010280
通常以2倍容量重新分配底層數(shù)據(jù),大批量添加數(shù)據(jù)時(shí),建議一次性分配足夠大的空間,減少內(nèi)存分配和數(shù)據(jù)復(fù)制開(kāi)銷(xiāo)。及時(shí)釋放不再使用的slice對(duì)象,避免持有過(guò)期數(shù)組,造成GC無(wú)法回收。
2.2. copy
函數(shù)copy在兩個(gè)slice間復(fù)制數(shù)據(jù),復(fù)制長(zhǎng)度以len小的為準(zhǔn)。
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s := data[8:]
s2 := data[:5]
fmt.Println(s2, s) // [0 1 2 3 4] [8 9]
copy(s2, s) // dst:s2, src:s
fmt.Println(s2) // [8 9 2 3 4]
fmt.Println(data) // [8 9 2 3 4 5 6 7 8 9]
3. Map
- 引用類(lèi)型,哈希;
- 鍵必須是支持相等運(yùn)算符的類(lèi)型,比如
number、string、pointer、array、struct以及對(duì)應(yīng)的interface。值可以是任何類(lèi)型,沒(méi)有限制; - 可以事先申請(qǐng)一大塊內(nèi)存,避免后續(xù)操作時(shí)頻繁擴(kuò)張
m := make(map[int]string, 1000); - 常見(jiàn)操作;
m := map[int]string{1: "Monday"} // 初始化
if v, ok := m[1]; ok { // 判斷k是否存在
println(v)
}
println(m[5]) // 對(duì)于不存在的key,直接返回\0,不會(huì)出錯(cuò)
m[2] = "Tuesday" // 新增或修改
delete(m, 0) //刪除,如果key不存在,不會(huì)出錯(cuò)
從map中取回的是value的臨時(shí)復(fù)制品,對(duì)其成員的修改沒(méi)有任何意義,正確做法是完整替換value或使用指針。
m := map[string]user{"user1": {"Jim", 10}}
m["user1"].age = 15 // error: cannot assign to struct field m["user1"].age in map
mp := map[string]*user{"user1": &user{"Jim", 10}}
mp["user1"].age = 15
fmt.Println(mp["user1"])
迭代時(shí)可以安全的刪除鍵值,不能新增鍵值。
4. Struct
- 值類(lèi)型,賦值和傳參都會(huì)復(fù)制全部?jī)?nèi)容;
- 可用
_定義補(bǔ)位字段,支持指向自身類(lèi)型的指針成員; - 順序初始化必須包含全部字段,否則會(huì)出錯(cuò);
- 支持匿名結(jié)構(gòu)
type File struct {
name string
size int
attr struct {
perm int
owner int
}
}
f := File{
name: "test.txt",
size: 1025,
// attr: {0755, 1}, // Error: missing type in composite literal
}
f.attr.owner = 1
f.attr.perm = 0755
var attr = struct {
perm int
owner int
}{2, 0755}
f.attr = attr
- 可定義字段標(biāo)簽,用反射讀取。標(biāo)簽是類(lèi)型的組成部分,無(wú)法比較;
var u1 struct {
name string "username"
}
var u2 struct{ name string }
u2 = u1 // Error: cannot use u1 (type struct { name string "username" }) as
// type struct { name string } in assignment
- 空結(jié)構(gòu)比較節(jié)省內(nèi)存,可以用來(lái)實(shí)現(xiàn)
set數(shù)據(jù)結(jié)構(gòu),或者實(shí)現(xiàn)沒(méi)有狀態(tài)只有方法的“靜態(tài)類(lèi)”;
set := map[string]struct{}{}
set["a"] = struct{}{}
4.1. 匿名字段
匿名字段是一種語(yǔ)法糖,從根本上說(shuō),就是一個(gè)與成員類(lèi)型同名的字段;被匿名嵌入的可以是任何類(lèi)型,當(dāng)然也包括指針??梢韵蚱胀ㄗ侄文菢釉L問(wèn)匿名字段成員,編譯器從外向內(nèi)逐級(jí)查找所有層次的匿名字段,直到發(fā)現(xiàn)目標(biāo)或者出錯(cuò)。
type User struct{ name string }
type Role struct {
User
role string
}
func main() {
r := Role{User: User{"Jim"}, role: "Administrator"} // 匿名字段的顯式字段名和類(lèi)型名相同
fmt.Println(r.name)
}
外層同名字段會(huì)遮蔽嵌入字段,相同層次的同名字段編譯器報(bào)錯(cuò);不能包含某一類(lèi)型和其指針類(lèi)型,因?yàn)槊窒嗤?/p>
type Resource struct {
id int
name string
}
type Classify struct {
id int
}
type User struct {
Resource // Resource.id 與 Classify.id 處于同?層次。
Classify
name string // 遮蔽 Resource.name。
}
func main() {
u := User{Resource{1, "people"},
Classify{100},
"Jack",
}
println(u.name) // User.name: Jack
println(u.Resource.name) // people
// println(u.id) // Error: ambiguous selector u.id
println(u.Classify.id) // 100
}
4.1. 面向?qū)ο?/h5>
面向?qū)ο蟮娜筇卣骼铮?code>go只支持封裝,盡管匿名字段的內(nèi)存布局和行為類(lèi)似繼承。