原文:CalliCoder — Golang Maps by Example
map 是沒有順序的鍵值對集合。它將鍵對應到值。鍵在 map 是唯一的,而值可能不是。
map 資料結構用於基於鍵的快速尋找、取得和刪除資料。它是電腦科學中最常用的資料結構之一。
Go 提供了內建的 map 型別。在本文中,我們將學到如何使用 Golang 內建的 map 型別。
宣告 map
使用以下語法宣告 map:
var m map[KeyType]ValueType
舉例來說,這是你如何宣告一個 string
鍵對應到 int
值的 map:
var m map[string]int
map 的零值是 nil
。nil
的 map 沒有鍵。此外,任何嘗試將鍵加到 nil
的 map 的行為將導致執行時期錯誤。
我們來看一個範例:
package main
import "fmt"
func main() {
var m map[string]int
fmt.Println(m)
if m == nil {
fmt.Println("m is nil")
}
// Attempting to add keys to a nil map will result in a runtime error
// m["one hundred"] = 100
}
# Output
map[]
m is nil
如果你取消陳述式 m["one hundred"] = 100
的註解,程式將產生以下錯誤:
panic: assignment to entry in nil map
因此,必需在加入項目之前初始化 map。
初始化 map
1. 使用內建的 make()
函數初始化 map
你可以使用內建的 make()
函數初始化 map。你只需要像下面的範例將 map 型別傳遞給 make()
函數即可。該函數將返回已初始化及可以使用的 map:
// Initializing a map using the built-in make() function
var m = make(map[string]int)
讓我們來看一個完整範例:
package main
import "fmt"
func main() {
var m = make(map[string]int)
fmt.Println(m)
if m == nil {
fmt.Println("m is nil")
} else {
fmt.Println("m is not nil")
}
// make() function returns an initialized and ready to use map.
// Since it is initialized, you can add new keys to it.
m["one hundred"] = 100
fmt.Println(m)
}
# Output
map[]
m is not nil
map[one hundred:100]
2. 使用 map 定數初始化 map
map 定數是使用某些資料初始化 map 的一種非常方便的方法。你只需要像這樣在大括號內傳遞以冒號分隔的鍵值對:
var m = map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
注意,最後一個逗號是必要的,否則將出現編譯器錯誤。
讓我們來看一個完整範例:
package main
import "fmt"
func main() {
var m = map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5, // Comma is necessary
}
fmt.Println(m)
}
# Output
map[one:1 two:2 three:3 four:4 five:5]
你也可以透過將大括號留空,使用 map 定數來建立一個空 map:
// Initialize an empty map
var m = map[string]int{}
上面的陳述式在功能上與使用 make()
函數相同。
加入項目(鍵值對)到 map
你可以使用以下語法將新項目加入到已初始化的 map:
m[key] = value
以下的範例使用 make()
函數初始化 map,並對它加入一些新項目:
package main
import "fmt"
func main() {
// Initializing a map
var tinderMatch = make(map[string]string)
// Adding keys to a map
tinderMatch["Rajeev"] = "Angelina" // Assigns the value "Angelina" to the key "Rajeev"
tinderMatch["James"] = "Sophia"
tinderMatch["David"] = "Emma"
fmt.Println(tinderMatch)
/*
Adding a key that already exists will simply override
the existing key with the new value
*/
tinderMatch["Rajeev"] = "Jennifer"
fmt.Println(tinderMatch)
}
# Output
map[Rajeev:Angelina James:Sophia David:Emma]
map[Rajeev:Jennifer James:Sophia David:Emma]
如果你嘗試加入一個已經存在於 map 中的鍵,那麼它將被新的值覆蓋。
取得 map 中特定鍵所關聯的值
你可以使用語法 m[key]
來取得分派給 map 中的鍵的值。如果鍵已經存在於 map,則將取得分派的值。否則,你將取得 map 的值型別的零值。
讓我們看個範例來了解這一點:
package main
import "fmt"
func main() {
var personMobileNo = map[string]string{
"John": "+33-8273658526",
"Steve": "+1-8579822345",
"David": "+44-9462834443",
}
var mobileNo = personMobileNo["Steve"]
fmt.Println("Steve's Mobile No : ", mobileNo)
// If a key doesn't exist in the map, we get the zero value of the value type
mobileNo = personMobileNo["Jack"]
fmt.Println("Jack's Mobile No : ", mobileNo)
}
# Output
Steve's Mobile No : +1-8579822345
Jack's Mobile No :
在上面的範例中,由於鍵 "Jack"
不存在於 map 中,我們得到 map 的值型別的零值。由於 map 的值型別是 string
,因此我們得到 ""
。
與其他語言不同,如果鍵不存在於 map 中,不會在 Golang 中出現執行時期錯誤。
但是,如果你要檢查 key 是否存在怎麼辦? 在上面的範例中,即使鍵 "Jack"
具有值為 ""
,map 也將返回 ""
。那麼,如何區別鍵的值與型別的零值相同和鍵不存在的情況?
嗯,讓我們來找出答案吧。
檢查鍵是否存在於 map
當你使用語法 map[key]
取得分派給特定鍵的值時,它也會返回一個額外的布林值,如果鍵存在於 map 中,則返回 true
,否則返回 false
。
因此,你可以使用以下雙值分派來檢查鍵是否存在於 map 中:
value, ok := m[key]
如果鍵存在,則布林變數 ok
將為 true
,否則為 false
。
細想以下 map 範例。它將 employeeIds 對應到名稱:
var employees = map[int]string{
1001: "Rajeev",
1002: "Sachin",
1003: "James",
}
由於鍵 1001
存在於 map 中,因此存取鍵 1001
會返回 "Rajeev"
和 true
:
name, ok := employees[1001] // "Rajeev", true
但是,如果你嘗試存取不存在的鍵,map 會返回一個空字串 ""
(字串的零值)和 false
:
name, ok := employees[1010] // "", false
如果你只是想檢查某個鍵是否存在而無需取得與該鍵關聯的值,那麼你可以使用 _
(底線)代替第一個值:
_, ok := employees[1005]
現在,讓我們看一個完整範例:
package main
import "fmt"
func main() {
var employees = map[int]string{
1001: "John",
1002: "Steve",
1003: "Maria",
}
printEmployee(employees, 1001)
printEmployee(employees, 1010)
if isEmployeeExists(employees, 1002) {
fmt.Println("EmployeeId 1002 found")
}
}
func printEmployee(employees map[int]string, employeeId int) {
if name, ok := employees[employeeId]; ok {
fmt.Printf("name = %s, ok = %v\n", name, ok)
} else {
fmt.Printf("EmployeeId %d not found\n", employeeId)
}
}
func isEmployeeExists(employees map[int]string, employeeId int) bool {
_, ok := employees[employeeId]
return ok
}
# Output
name = Rajeev, ok = true
EmployeeId 1010 not found
EmployeeId 1002 found
在上面的範例中,我在 if
陳述式中使用了簡短宣告來初始化 name
和 ok
值,然後測試布林值 ok
。它使程式碼更加簡潔。
從 map 中刪除鍵
你可以使用內建的 delete()
函數從 map 中刪除鍵。語法如下:
// Delete the `key` from the `map`
delete(map, key)
delete()
函數不會返回任何值。另外,如果該鍵不存在於 map,它也不會執行任何操作。
這是一個完整範例:
package main
import "fmt"
func main() {
var fileExtensions = map[string]string{
"Python": ".py",
"C++": ".cpp",
"Java": ".java",
"Golang": ".go",
"Kotlin": ".kt",
}
fmt.Println(fileExtensions)
delete(fileExtensions, "Kotlin")
// delete function doesn't do anything if the key doesn't exist
delete(fileExtensions, "Javascript")
fmt.Println(fileExtensions)
}
# Output
map[Python:.py C++:.cpp Java:.java Golang:.go Kotlin:.kt]
map[Python:.py C++:.cpp Java:.java Golang:.go]
map 是參考型別
map 是參考型別。當你將 map 分派給新變數時,它們都參考相同的底層資料結構。因此,一個變數完成的更改另一個變數將會看到。
package main
import "fmt"
func main() {
var m1 = map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5,
}
var m2 = m1
fmt.Println("m1 = ", m1)
fmt.Println("m2 = ", m2)
m2["ten"] = 10
fmt.Println("\nm1 = ", m1)
fmt.Println("m2 = ", m2)
}
# Output
m1 = map[one:1 two:2 three:3 four:4 five:5]
m2 = map[one:1 two:2 three:3 four:4 five:5]
m1 = map[one:1 two:2 three:3 four:4 five:5 ten:10]
m2 = map[one:1 two:2 three:3 four:4 five:5 ten:10]
將 map 傳遞給函數時,會套用相同概念。函數內部對 map 所做的任何更改,呼叫者也看得到。
迭代 map
你可以使用 range
格式的 for 迴圈迭代 map。它在每次迭代中會給你 key, value
:
package main
import "fmt"
func main() {
var personAge = map[string]int{
"Rajeev": 25,
"James": 32,
"Sarah": 29,
}
for name, age := range personAge {
fmt.Println(name, age)
}
}
# Output
James 32
Sarah 29
Rajeev 25
注意,map 是沒有順序的集合,因此每次迭代時,不能保證 map 的迭代順序都相同。
因此,如果多次執行上面的程式,你將得到不同順序的結果。
結論
在本文中,你學到了如何宣告和初始化 map、如何將鍵加到 map、如何取得 map 中特定鍵所關聯的值、如何檢查鍵是否存在於 map 中、如何從 map 中刪除鍵,以及如何迭代 map。