2022-12-29_23_58-go学习笔记(13)map

go语言针对数据映射的情况原生提供了map数据类型,可以这样定义一个map:map[键类型]值类型。

map的定义声明

直接用var定义的map是nil的map,其值为nil。对nil的map进行读操作总是会返回对应的零值,进行写操作会导致panic。

1
var m map[string]int

所以如果想要使用map,就不能使用值为nil的map。

可以通过:=快速创建一个字面量为空的map,字面量为空的map不是nil的map。可能有些绕,把它类比成指针就比较好理解了,nil的map就相当于一个空指针,字面量为空的map就相当于这个指针指向了一个空白的区域。

1
m:=map[string]int{}

如果map在定义时已经有现成的键值对数据,也可以直接在定义时用非空的字面量来创建map。最后一个键值对后面要么直接跟}收尾,要么与前面一致用逗号分隔然后另起一行用}收尾。

1
2
3
4
5
6
7
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
fmt.Println(m["one"])
fmt.Println(m["two"])

输出:

1
2
1
2

可以用make来创建map,如果预先知道map大致容量也可以在make时设置初始容量,减少扩容次数。

1
2
3
4
var m map[string]int
m = make(map[string]int,2)
m["one"] = 1
fmt.Println(m["one"])

输出:

1
1

mapslice有很多相似的地方:

  • map在添加键值对时会自动扩容
  • 在知道有多少键值对时,可以用make指定map的初始容量
  • 通过len函数可以获取map中键值对个数
  • map的零值是nil
  • 两个map之间不能比较。

map的键必须是可比较的类型,我们不能用slice或map等不可比较的类型作为map的键。

如何判断键是否存在

map对不存在的键会返回其对应值的零值,这样我们就不能通过其返回值来判断相应的键是否存在。

在获取map中键对应的值的时候,可以通过其第二个返回值来判断。

1
2
3
4
5
6
m := map[string]int{
"one": 1,
"two": 2,
}
three, ok := m["three"]
fmt.Println(three, ok)

输出:

1
0 false

第二个返回值为false就说明键不存在了。

删除map中的键值对

通过go语言内置的delete函数可以删除键值对。

1
2
3
4
5
6
7
m := map[string]int{
"one": 1,
"two": 2,
}
delete(m, "one")
one, ok := m["one"]
fmt.Println(one, ok)

输出:

1
0 false

将map当成set来用

很多语言中有集合类型,但go语言中没有。如果不需要集合交并运算,只需要去重和判断数据存不存在的话,我们可以把map当成set来用。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 假如我们要用set对一组数据去重,得到不重复数据的个数
// 并判断10在不在这组数据中
nums := []int{
1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 7,
}
s := map[int]bool{}
for _, num := range nums {
s[num] = true
}
cnt := len(s)
fmt.Println(cnt)
existTen := s[10]
fmt.Println(existTen)

输出:

1
2
7
false

也可以用空结构体struct{}替换bool实现set,用空结构体的好处是空结构体不占内存,适用于数据量很大的情况。缺点是写起来比较麻烦。

1
2
3
4
5
6
7
8
9
10
11
nums := []int{
1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 7,
}
s := map[int]struct{}{}
for _, num := range nums {
s[num] = struct{}{}
}
cnt := len(s)
fmt.Println(cnt)
_,existTen := s[10]
fmt.Println(existTen)

输出:

1
2
7
false

map键值对中结构体数据的更新

如果有其他语言经验的,如用过c++标准库中的map,很可能会想当然地认为可以直接修改键值对的值。

在c++中可以像下面这样:

1
2
3
4
5
6
7
8
9
struct point
{
int x;
int y;
};
map<string,point> mp;
mp["a"]=point{1,1};
mp["a"].x=4;
cout <<mp["a"].x<<" "<<mp["a"].y<<endl;

输出:

1
4 1

可以看到mp["a"]的值被改了。

但在go语言中不能这样做,会直接报错。
366a4655ff60303a00fe3383989b0739.png
只能用新的值去覆盖原来的值:

1
2
3
4
5
6
7
8
9
10
11
type Point struct {
X int
Y int
}
m := map[string]Point{
"A": {1, 1},
}
p := m["A"]
p.X = 4
m["A"] = p
fmt.Println(m["A"])

输出:

1
{4 1}

江达小记