Struct
在前面提到过,struct是值类型,所以支持 "=="、"!=" 相等操作符
由于是值类型,复制和传参会复制全部内容。可用 "_" 定义补位字段,支持指向自身类型的指针成员
//声明一个结构体
type Node struct {
//_ int 补位字段
id int
data *byte
next *Node
}
初始化
对于上面提到的结构体的初始化
//普通初始化
n1 := Node{
id: 1,
data: nil,
}
//顺序初始化
n2 := Node{1,nil,nil} //顺序初始化必须包含全部字段,否则会出错。
如果结构体包含有补位字段则不能使用顺序初始化
匿名结构体
type File struct {
name string
size int
attr struct { //匿名结构体做结构体的成员
perm int
owner int
}
}
func main(){
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 {
porm int
owner int
}{2,0755}
f.attr = attr
匿名字段
匿名字段不过是一种语法糖,从根本上说,就是一个与成员类型同名 (不含包名) 的字段。被匿名嵌入的可以是任何类型,当然也包括指针。
type User struct {
name string
}
type Manager struct {
User //同名字段,类型和变量名称都是User
title string
}
m := Manager{
User: User{"Tom"}, // 匿名字段的显式字段名,和类型名相同。
title: "Administrator",
}
可以像普通字段那样访问匿名字段成员,编译器从外向内逐级查找所有层次的匿名字段,直到发现目标或出错。
type Resource struct {
id int
}
type User struct {
Resource
name string
}
type Manager struct {
User
title string
}
var m Manager
m.id = 1
m.name = "Jack"
m.title = "Administrator"
Map
引用类型,哈希表(key-value)。
key:必须支持相等运算符(==、!=),比如 number
、string
、pointer
、array
、struct
,以及对应的 interface
value:可以是任意类型,没有限制
m := map[int]struct {
name string
age int
}{
1: {"user1", 10}, // 可省略元素类型。
2: {"user2", 20},
}
println(m[1].name)
预先给 make
函数一个合理元素数量参数,有助于提升性能。因为事先申请一大块内存,可避免后续操作时频繁扩张。
m := make(map[string]int, 1000)
常见操作
对于一个Map
m := map[int]string{1:"hello"}
检查Map中是否包含某元素
//v:如果有值为1的key则返回对应的值,若没有则返回该类型默认值
//ok:是否含有值为1的key,bool类型
if v,ok := m[1];ok {
...
}
新增或修改
//如果有值为2的key则添加,否则为修改
m[2] = "data"
删除
//删除m中key为2的键值对,如果不存在也不会出错
delete(m,2)
获取Map长度
//注意,cap对map无效!!
len(m)
遍历/迭代
不能保证迭代返回的次序,通常是随即结果
//k:key
//v:value
for k,v := range m{
fmt.Println(k,v)
}
从map中取得的value为原数据的副本
从 map
中取回的是一个 value
临时复制品,对其成员的修改是没有任何意义的。
type user struct{ name string }
m := map[int]user{ // 当 map 因扩张而重新哈希时,各键值项存储位置都会发生改变。 因此,map
1: {"user1"}, // 被设计成 not addressable。 类似 m[1].name 这种期望透过原 value
} // 指针修改成员的行为自然会被禁止。
m[1].name = "Tom" // Error: cannot assign to m[1].name
正确做法是完整替换value
m := map[int]user{
1: {"user1"},
}
//完整替换
m[1] = user{"user2"}
或map的value使用指针
//使用指针
m2 := map[int]*user{
1:{"user1"},
}
m2[1].name = "user2" //返回的是指针复制品,头故宫指针修改原对象是允许的
//回顾:直接用 "." 访问目标成员(相较C简化了使用,不再需要将指针转换为目标实体)。 (Let’s Go 3 — 指针与自定义类型)
如果迭代同时进行增删
可以在迭代时安全删除键值。但如果期间有新增操作,那么就不知道会有什么意外了。
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
m := map[int]string{
0: "a", 1: "a", 2: "a", 3: "a", 4: "a",
5: "a", 6: "a", 7: "a", 8: "a", 9: "a",
}
for k := range m {
m[k+k] = "x"
delete(m, k)
}
fmt.Println(m)
}
}
/*
输出:
map[2:x 6:x 10:x 12:x 14:x 18:x 32:x]
map[2:x 4:x 10:x 12:x 14:x 18:x 32:x]
map[12:x 16:x 18:x 20:x 28:x]
map[8:x 10:x 12:x 16:x 18:x 28:x]
map[2:x 8:x 10:x 12:x 14:x 18:x 32:x]
每次执行都不同
*/
参考
Q.E.D.