數(shù)據(jù)

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;
  • lencap都返回?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,那么lencap都為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)似繼承。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容