码农桃花源
GitHub
知乎
掘金
博客园
Search…
码农桃花源
README
channel
map
map 的底层实现原理是什么
可以边遍历边删除吗
map 的删除过程是怎样的
可以对 map 的元素取地址吗
如何比较两个 map 相等
如何实现两种 get 操作
map 是线程安全的吗
map 的遍历过程是怎样的
map 中的 key 为什么是无序的
float 类型可以作为 map 的 key 吗
map 的赋值过程是怎样的
map 的扩容过程是怎样的
interface
标准库
goroutine 调度器
编译和链接
反射
数组和切片
GC
Powered By
GitBook
如何实现两种 get 操作
Go 语言中读取 map 有两种语法:带 comma 和 不带 comma。当要查询的 key 不在 map 里,带 comma 的用法会返回一个 bool 型变量提示 key 是否在 map 中;而不带 comma 的语句则会返回一个 key 类型的零值。如果 key 是 int 型就会返回 0,如果 key 是 string 类型,就会返回空字符串。
1
package
main
2
3
import
"fmt"
4
5
func
main
()
{
6
ageMap
:=
make
(
map
[
string
]
int
)
7
ageMap
[
"qcrao"
]
=
18
8
9
// 不带 comma 用法
10
age1
:=
ageMap
[
"stefno"
]
11
fmt
.
Println
(
age1
)
12
13
// 带 comma 用法
14
age2
,
ok
:=
ageMap
[
"stefno"
]
15
fmt
.
Println
(
age2
,
ok
)
16
}
Copied!
运行结果:
1
0
2
0 false
Copied!
以前一直觉得好神奇,怎么实现的?这其实是编译器在背后做的工作:分析代码后,将两种语法对应到底层两个不同的函数。
1
// src/runtime/hashmap.go
2
func
mapaccess1
(
t
*
maptype
,
h
*
hmap
,
key unsafe
.
Pointer
)
unsafe
.
Pointer
3
func
mapaccess2
(
t
*
maptype
,
h
*
hmap
,
key unsafe
.
Pointer
)
(
unsafe
.
Pointer
,
bool
)
Copied!
源码里,函数命名不拘小节,直接带上后缀 1,2,完全不理会《代码大全》里的那一套命名的做法。从上面两个函数的声明也可以看出差别了,
mapaccess2
函数返回值多了一个 bool 型变量,两者的代码也是完全一样的,只是在返回值后面多加了一个 false 或者 true。
另外,根据 key 的不同类型,编译器还会将查找、插入、删除的函数用更具体的函数替换,以优化效率:
key 类型
查找
uint32
mapaccess1_fast32(t
maptype, h
hmap, key uint32) unsafe.Pointer
uint32
mapaccess2_fast32(t
maptype, h
hmap, key uint32) (unsafe.Pointer, bool)
uint64
mapaccess1_fast64(t
maptype, h
hmap, key uint64) unsafe.Pointer
uint64
mapaccess2_fast64(t
maptype, h
hmap, key uint64) (unsafe.Pointer, bool)
string
mapaccess1_faststr(t
maptype, h
hmap, ky string) unsafe.Pointer
string
mapaccess2_faststr(t
maptype, h
hmap, ky string) (unsafe.Pointer, bool)
这些函数的参数类型直接是具体的 uint32、unt64、string,在函数内部由于提前知晓了 key 的类型,所以内存布局是很清楚的,因此能节省很多操作,提高效率。
上面这些函数都是在文件
src/runtime/hashmap_fast.go
里。
Previous
如何比较两个 map 相等
Next
map 是线程安全的吗
Last modified
2yr ago
Copy link