文章目录
- map
- 增加和更新
- 删除
- 查询
- 遍历(for-range)
- map切片
- 关于哈希表遍历的一点看法
- 对map的key排序
- 结构体与OOP
- 声明、初始化、序列化
- 方法
- 工厂模式
- 个人博客:CSDN博客
map
-
map是一个key-value的数据结构,又称为字段或关联数组
-
Golang自带的map是哈希表
-
声明
-
import "fmt" func main() { var a map[int]int fmt.Println(a) }
-
slice,map和func不能作为键值
-
声明map是不会分配内存的,初始化需要用make
-
import "fmt" func main() { var a map[int]int a = make(map[int]int, 3)//可以存放三个键值对 fmt.Println(a) }
-
Go的map的键值是没有顺序的
-
自动增长
-
func main() { a := make(map[int]int) a[1] = 2 a[2] = 1 fmt.Println(a) }
-
直接初始化
-
func main() { a := map[int]int{ 1: 1, 2: 2,//这里也要, } fmt.Println(a) }
增加和更新
- 直接给键值赋值即可
删除
-
delete(map, 1)//删除map键值为1的对,如果不存在不会操作
-
如果要完全清空
-
遍历key来删除,或者让map赋值一个新的map,给GC回收
查询
val, flag := mp[1] //flag是bool,找到是true,没找到是false,val是对应值
遍历(for-range)
-
func main() { a := map[int]int{ 1: 1, 2: 2, } for k, v := range a { fmt.Println(k, v) } fmt.Println(a) }
map切片
- 同样slice的用法,注意map也要make就行,相当于在底层维护一个map类型的数组
关于哈希表遍历的一点看法
- 大概是这样的,在底层有一个迭代器链表,或者,有一个指针数组,每次存放一个指针,迭代器依次访问这些指针,但是在添加元素的时候, 会rehash,导致顺序变化。所以Go的设计者把他设置成无序,每次都打乱这个数组,防止程序员误用哈希map
对map的key排序
-
将键值追加到切片内,然后对切片排序
-
import ( "fmt" "sort" ) func main() { mp := make(map[int]int, 10) mp[1] = 2 mp[3] = 1 mp[2] = 5 mp[5] = 6 var keys []int //切片 for key, _ := range mp { keys = append(keys, key) } sort.Ints(keys) fmt.Println(keys) }
-
map是引用类型
结构体与OOP
声明、初始化、序列化
-
go语言是用struct来面向对象的
-
声明
-
type Node struct { X int Y int } func main() { var a Node//空间自动分配 fmt.Println(a) } //输出{0 0}
-
赋值
-
func main() { var a Node a.X = 1 a.Y = 2 fmt.Println(a) } //输出{1 2}
-
初始化
-
//方法一 a := Node{1, 2} //方法二 ptr *Node = new(Node) (*ptr).X = 1 (*ptr).Y = 2 //-------- func main() { var ptr *Node = new(Node) (*ptr).X = 1 (*ptr).Y = 2 fmt.Println(*ptr) } //---底层自动识别,这样也可以 func main() { var ptr *Node = new(Node) ptr.X = 1 ptr.Y = 2 fmt.Println(*ptr) } //方法三 var node *Node = &Node{}
-
结构体的所有字段在内存中是连续的
-
两个结构体的字段类型完全相同的话可以强制类型转换
-
用type struct1 struct2给struct2取别名,相当于定义了一个新的类型,两者之间可以强制类型转换,但是不能直接赋值
-
struct的每个字段上可以写上一个tag,该tag可以通过反射机制获取,常见于序列化和反序列化
-
复习一个点,Go没有public和private,所以用首字母大写和小写来确定是公共的还是私有的
-
序列化:对象转json字符串
-
反序列化:json字符串转对象
-
动手
-
import ( "encoding/json" "fmt" ) type Node struct { Name string Password string } func main() { a := Node{ "aaaaaa", "123456", } str, _ := json.Marshal(a) fmt.Println(a) fmt.Println(string(str)) } //输出 //{aaaaaa 123456} //{"Name":"aaaaaa","Password":"123456"}
-
但是这种大写的变量很多客户端不习惯,所以使用tag,如果使用小写,就无法被结构体外部函数使用,无法序列化
-
import ( "encoding/json" "fmt" ) type Node struct { Name string `json:"name"` Password string `json:"password"` } func main() { a := Node{ "aaaaaa", "123456", } str, _ := json.Marshal(a) fmt.Println(a) fmt.Println(string(str)) } //输出 //{aaaaaa 123456} //{"name":"aaaaaa","password":"123456"}
方法
-
方法是作用在指定类型上的函数
-
func (a Node) ok() { fmt.Println("ok") } //这个函数绑定给了Node //调用 var a Node p.ok()
-
动手
-
import ( "fmt" ) type Node struct { Name string `json:"name"` Password string `json:"password"` } func (a Node) ok() { fmt.Println(a.Name) } func main() { a := Node{ "aaaaaa", "123456", } a.ok() } //输出了Node的名字
-
这个方法只能用指定类型来调用,不能直接调用
-
如果想要修改原来的参数,我们使用结构体指针,并且这更常用,不用深拷贝,速度更快
-
type Node struct { Name string `json:"name"` Password string `json:"password"` } func (a *Node) ok() { a.Name = "bbbb" } func main() { a := Node{ "aaaaaa", "123456", } a.ok()//编译器底层自动识别变为&a fmt.Println(a) }
-
可以通过实现String方法,可以自定义格式化输出
工厂模式
- 类似于构造函数,在结构体所在包下写相应构造的函数,返回结构体指针,这样就可以在结构体私有的情况下,在其他包调用这个函数直接返回结构体对象