一開始用 Go 的 Map 時常常搞不清楚到底要不要傳指標,好像大部分的時候都不用,但用到 unmarshal 之類的 function 就又要傳指標進去了。
這篇就來研究一下 Go 的 Map 到底是什麼生物
先看看下面這段程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// mv = map value
func ModifyMap(mv map[int]int) {
mv[1] = 1
}
// mp = map pointer
func ModifyMapByPointer(mp *map[int]int) {
(*mp)[1] = 1
}
func main() {
m1 := make(map[int]int)
ModifyMap(m1)
fmt.Println(m1) // map[1:1]
m2 := make(map[int]int)
ModifyMapByPointer(&m2)
fmt.Println(m2) // map[1:1]
}
|
兩個 function 都改到 map 裡面的值了,所以用不用指標到底有沒有差呢?
再看看下面這段程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
func MakeMap(mv map[int]int) {
mv = make(map[int]int)
mv[1] = 1
}
func MakeMapByPointer(mp *map[int]int) {
(*mp) = make(map[int]int)
(*mp)[1] = 1
}
func main() {
var m1 map[int]int
MakeMap(m1)
fmt.Println(m1) // map[]
var m2 map[int]int
MakeMapByPointer(&m2)
fmt.Println(m2) // map[1:1]
}
|
這次的 MakeMap 的操作就沒有反應到 main 傳進去的 m1 上,這是怎麼回事呢?
首先當我們使用 make 產生 map 時,它就是回傳一個 pointer 給我們。也就是說當我們呼叫了 m := make(map[int]int)
,m 這個變數內存的其實是一個地址,指到真正的 map struct。
來看看第一組程式碼發生了什麼事
簡單來說就是 m1 把 map 的地址 x 傳進去 ModifyMap,mv 內就存著相同的值 x,所以可以改到真正的 map struct。
1
2
3
4
5
6
7
|
func ModifyMap(mv map[int]int) {
mv[1] = 1
}
m1 := make(map[int]int)
ModifyMap(m1)
fmt.Println(m1) // map[1:1]
|
下面這部分,是把 m2 本身的位置 y 傳進 ModifyMapByPointer, mp 就存著 m2 的位置 y。
接著透過指標改到了 map struct
1
2
3
4
5
6
7
|
func ModifyMapByPointer(mp *map[int]int) {
(*mp)[1] = 1
}
m2 := make(map[int]int)
ModifyMapByPointer(&m2)
fmt.Println(m2) // map[1:1]
|
再來看看在 function 裡面 make map 會怎麼樣
這裡 m1 一開始是 nil,傳進 MakeMap 的東西也是 nil,自然 mv 內也會存著 nil。
接著呼叫了 make,mv = make(map[int]int)
把真正 map struct 的地址存到了 mv 內,mv[1] = 1
也是真的有改到 map 裡面的值,只是外面的 m1 內存的值還是 nil,所以我們 print 出來的也就是空 map 了。
1
2
3
4
5
6
7
8
|
func MakeMap(mv map[int]int) {
mv = make(map[int]int)
mv[1] = 1
}
var m1 map[int]int
MakeMap(m1)
fmt.Println(m1) // map[]
|
最後一部分的範例,m2 的地址 y 傳進了 MakeMapByPointer,mp 內存著的是 m2 的地址,當呼叫(*mp) = make(map[int]int)
時所代表的是產生一個 map struct,並把該地址 x 存進 mp 所指到的變數內,也就是 m2 的值中,所以後續的更改我們也可以從 m2 看到拉
1
2
3
4
5
6
7
8
|
func MakeMapByPointer(mp *map[int]int) {
(*mp) = make(map[int]int)
(*mp)[1] = 1
}
var m2 map[int]int
MakeMapByPointer(&m2)
fmt.Println(m2) // map[1:1]
|
至於真正的 map struct 是一個叫做 hmap 的東西,這邊就不講那麼細,剩下的上網估狗 Go makemap hmap 之類的關鍵字都可以找到嚕