Calvert's murmur

Golang map 介紹

2019-11-14

約 6077 字 / 需 33 分鐘閱讀

原文:CalliCoderGolang 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 的零值nilnil 的 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 陳述式中使用了簡短宣告來初始化 nameok 值,然後測試布林值 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。

Tags: Golang