数据

1. Array

  • 数组是值类型,赋值和传参都会拷贝整个数组;
  • 数组长度必须是常量,且是类型的组成部分,[2]int[3]int是不同类型;
  • 支持==!=操作符;
  • 指针数组[10]*int,数组指针*[10]int;
  • lencap都返回数组长度;
  • 可用复合语句初始化;
a := [3]int{1, 2}          // 未初始化的元素值为0
b := [...]int{1, 2, 3, 4}  // 通过初始化值确定长度
c := [5]int{2: 100, 3: 10} //使用索引号初始化元素
d := [...]struct {
    name string
    age  int
}{
    {"Jim", 10},
    {"Jack", 11}, // 别忘了最后一行的逗号
}
e := [2][3]int{{1, 2, 3}, {4, 5, 6}}
f := [...][3]int{{1, 2, 3}, {4, 5, 6}} // 第二维不可用...

值拷贝会造成性能问题,通常建议用slice或者数组指针。

2. slice

slice并不是数组或者数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

  • 引用类型,自身是结构体,值拷贝传递;
type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}
  • 属性len表示可用元素数量,读写操作不能够超过该限制;
  • 属性cap表示最大容量,不能超过数组限制;
  • 如果slice == nil,那么lencap都为0;
    创建slice时,索引不能超过数组限制,读写slice操作的是底层数组。
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
// s := a[1:9:9] // invalid slice index 9 索引不能超过数组限制
s := a[1:4:5]
s[2] = 10
fmt.Println(a, s) // [1 2 3 10 5 6 7 8] [2 3 10]
  • 可直接创建slice对象,自动分配底层数组。
a := []int{1, 2, 3, 4, 8: 9}   // 初始化创建,可使用索引号
fmt.Println(a, len(a), cap(a)) // [1 2 3 4 0 0 0 0 9] 9 9
b := make([]int, 6)            // make创建,省略cap,相当于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创建新slice对象
2.1. append

slice尾部添加数据,返回新的slice对象。一旦slice超出cap限制,就会重新分配底层数组,即便原数组并未填满。

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两个值,超出s.cap限制
fmt.Println(&s[0], &data[0]) // 0xc00000a2d0 0xc000010280

通常以2倍容量重新分配底层数据,大批量添加数据时,建议一次性分配足够大的空间,减少内存分配和数据复制开销。及时释放不再使用的slice对象,避免持有过期数组,造成GC无法回收。

2.2. copy

函数copy在两个slice间复制数据,复制长度以len小的为准。

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

  • 引用类型,哈希;
  • 键必须是支持相等运算符的类型,比如numberstringpointerarraystruct以及对应的interface。值可以是任何类型,没有限制;
  • 可以事先申请一大块内存,避免后续操作时频繁扩张m := make(map[int]string, 1000)
  • 常见操作;
m := map[int]string{1: "Monday"} // 初始化

if v, ok := m[1]; ok { // 判断k是否存在
    println(v)
}

println(m[5]) // 对于不存在的key,直接返回\0,不会出错

m[2] = "Tuesday" // 新增或修改

delete(m, 0) //删除,如果key不存在,不会出错

map中取回的是value的临时复制品,对其成员的修改没有任何意义,正确做法是完整替换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"])

迭代时可以安全的删除键值,不能新增键值。

4. Struct

  • 值类型,赋值和传参都会复制全部内容;
  • 可用_定义补位字段,支持指向自身类型的指针成员;
  • 顺序初始化必须包含全部字段,否则会出错;
  • 支持匿名结构
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
  • 可定义字段标签,用反射读取。标签是类型的组成部分,无法比较;
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
  • 空结构比较节省内存,可以用来实现set数据结构,或者实现没有状态只有方法的“静态类”;
set := map[string]struct{}{}
set["a"] = struct{}{}
4.1. 匿名字段

匿名字段是一种语法糖,从根本上说,就是一个与成员类型同名的字段;被匿名嵌入的可以是任何类型,当然也包括指针。可以向普通字段那样访问匿名字段成员,编译器从外向内逐级查找所有层次的匿名字段,直到发现目标或者出错。

type User struct{ name string }

type Role struct {
    User
    role string
}

func main() {
    r := Role{User: User{"Jim"}, role: "Administrator"} // 匿名字段的显式字段名和类型名相同
    fmt.Println(r.name)
}

外层同名字段会遮蔽嵌入字段,相同层次的同名字段编译器报错;不能包含某一类型和其指针类型,因为名字相同;

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. 面向对象

面向对象的三大特征里,go只支持封装,尽管匿名字段的内存布局和行为类似继承。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355