文章目录
map
-
map是一个key-value的数据结构,又称为字段或关联数组
-
Golang自带的map是哈希表
-
声明
go
import "fmt"
func main() {
var a map[int]int
fmt.Println(a)
}
-
slice,map和func不能作为键值
-
声明map是不会分配内存的,初始化需要用make
go
import "fmt"
func main() {
var a map[int]int
a = make(map[int]int, 3)//可以存放三个键值对
fmt.Println(a)
}
-
Go的map的键值是没有顺序的
-
自动增长
go
func main() {
a := make(map[int]int)
a[1] = 2
a[2] = 1
fmt.Println(a)
}
-
直接初始化
go
func main() {
a := map[int]int{
1: 1,
2: 2,//这里也要,
}
fmt.Println(a)
}
增加和更新
- 直接给键值赋值即可
删除
go
delete(map, 1)//删除map键值为1的对,如果不存在不会操作
-
如果要完全清空
-
遍历key来删除,或者让map赋值一个新的map,给GC回收
查询
go
val, flag := mp[1] //flag是bool,找到是true,没找到是false,val是对应值
遍历(for-range)
go
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排序
-
将键值追加到切片内,然后对切片排序
go
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来面向对象的
-
声明
go
type Node struct {
X int
Y int
}
func main() {
var a Node//空间自动分配
fmt.Println(a)
}
//输出{0 0}
-
赋值
go
func main() {
var a Node
a.X = 1
a.Y = 2
fmt.Println(a)
}
//输出{1 2}
-
初始化
go
//方法一
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字符串转对象
-
动手
go
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,如果使用小写,就无法被结构体外部函数使用,无法序列化
go
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"}
方法
-
方法是作用在指定类型上的函数
go
func (a Node) ok() {
fmt.Println("ok")
}
//这个函数绑定给了Node
//调用
var a Node
p.ok()
-
动手
go
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的名字
-
这个方法只能用指定类型来调用,不能直接调用
-
如果想要修改原来的参数,我们使用结构体指针,并且这更常用,不用深拷贝,速度更快
go
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方法,可以自定义格式化输出
工厂模式
- 类似于构造函数,在结构体所在包下写相应构造的函数,返回结构体指针,这样就可以在结构体私有的情况下,在其他包调用这个函数直接返回结构体对象