1. 程式人生 > 實用技巧 >golang學習筆記---對映(map)

golang學習筆記---對映(map)

對映(map)

對映是一種資料結構,用於儲存一系列無序的鍵值對,它基於鍵來儲存值。對映的特點是能夠基於鍵快速檢索資料。鍵就像是陣列的索引一樣,指向與鍵關聯的值。
與 C++、Java 等程式語言不同,在 Golang 中使用對映不需要引入任何庫。因此 Golang 的對映使用起來更加方便。我們可以通過下圖簡要的理解一下對映中鍵值對的關係:

圖中的每個鍵值對錶示一種顏色的字串名稱及其對應的十六進位制值,其中名稱為鍵,十六進位制數為值。

建立和初始化對映

Golang 中有很多種方法可以建立並初始化對映,可以使用內建的 make 函式,也可以使用對映字面量。

使用 make 函式宣告對映

// 建立一個對映,鍵的型別是 string,值的型別是 int
myMap := make(map[string]int)

使用字面量宣告對映

// 建立一個對映,鍵和值的型別都是 string
// 使用兩個鍵值對初始化對映
myMap := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}

建立對映時,更常用的方法是使用對映字面量。對映的初始長度會根據初始化時指定的鍵值對的數量來確定。

對映的鍵可以是任何值
這個值的型別可以是內建的型別,也可以是結構型別,只要這個值可以使用 == 運算子做比較。切片、函式以及包含切片的結構型別,這些型別由於具有引用語義,不能作為對映的鍵,使用這些型別會造成編譯錯誤:

// 使用對映字面量宣告空對映
// 建立一個對映,使用字串切片作為對映的鍵
myMap := map[[]string]int{}

如果你使用的 IDE 支援語法檢查,就會提示這段程式碼有語法錯誤:

如果直接編譯上面的程式碼,會得到一個編譯時錯誤:
invalid map key type []string

雖然切片不能作為對映的鍵,但是卻可以作為對映的值,這個在使用一個對映鍵對應一組資料時,會非常有用:

// 宣告一個儲存字串切片的對映
// 建立一個對映,使用字串切片作為值
myMap := map[int][]string{}

元素賦值

通過指定適當型別的鍵並給這個鍵賦一個值就完成了對映的鍵值對賦值:

// 建立一個空對映,用來儲存顏色以及顏色對應的十六進位制程式碼
myColors := map[string]string{}
// 將 Red 的程式碼加入到對映
myColors["Red"] = "#da1337"

與切片類似,可以通過宣告一個未初始化的對映來建立一個值為 nil 的對映(一般稱為 nil 對映),nil 對映不能用於儲存鍵值對:

// 通過宣告對映建立一個 nil 對映
var myColors map[string]string
// 將 Red 的程式碼加入到對映
myColors["Red"] = "#da1337"

執行這段程式碼會產生一個執行時錯誤:
panic: assignment to entry in nil map

查詢與遍歷

測試鍵值是否存在
查詢對映裡是否存在某個鍵是對映的一個基本操作。這個操作往往需要使用者寫一些邏輯程式碼,根據邏輯程式碼確定是否完成了某個操作或者對映裡是否快取了某些資料。查詢操作也可以用來比較兩個對映,確定哪些鍵值對互相匹配,哪些鍵值對不匹配。
有兩種方法可以檢查鍵值對是否存在,第一種方式是獲取鍵值對中的值以及一個表示這個鍵是否存在的布林型別標誌:

// 獲取鍵 Blue 對應的值
value, exists := myColors["Blue"]
// 這個鍵存在嗎?
if exists {
    fmt.Println(value)
}

另一中方式是,只返回鍵對應的值,然後通過判斷這個值是不是零值來確定鍵是否存在:

// 獲取鍵 Blue 對應的值
value := myColors["Blue"]
// 這個鍵存在嗎?
if value != "" {
    fmt.Println(value)
}

顯然,這種方式只能用在對映儲存的值都是非零值的情況下。
注意:在 Golang 中,通過鍵來索引對映時,即便這個鍵不存在也總會返回一個值。在這種情況下,返回的是該值對應的型別的零值。

遍歷對映
和遍歷陣列、切片一樣,使用關鍵字 range 可以遍歷對映中的所有值。但對對映來說,range 返回的不是索引和值,而是鍵值對:

// 建立一個對映,儲存顏色以及顏色對應的十六進位制程式碼
myColors := map[string]string{
    "AliceBlue":"#f0f8ff",
    "Coral":"#ff7F50",
    "DarkGray":"#a9a9a9",
    "ForestGreen": "#228b22",
}
// 顯示對映裡的所有顏色
for key, value := range myColors {
    fmt.Printf("Key: %s  Value: %s\n", key, value)
}

執行上面的程式碼,輸出如下:

Key: AliceBlue  Value: #f0f8ff
Key: Coral  Value: #ff7F50
Key: DarkGray  Value: #a9a9a9
Key: ForestGreen  Value: #228b22

刪除對映中的元素

Golang 提供了一個內建的函式 delete() 用於刪除集合中的元素,下面是一個簡單的例子:
delete(myMap, "hello")
上面的程式碼將從 myMap 中刪除鍵為 hello 的鍵值對。如果 hello 這個鍵不存在,那麼這個呼叫將什麼都不會發生,也不會有什麼副作用。但是如果傳入的對映的變數的值為 nil,該呼叫將導致程式丟擲異常(panic)。
還以前面定義的 myColors 對映為例,我們用 delete() 函式刪除其中的 Coral:

// 建立一個對映,儲存顏色以及顏色對應的十六進位制程式碼
myColors := map[string]string{
    "AliceBlue":"#f0f8ff",
    "Coral":"#ff7F50",
    "DarkGray":"#a9a9a9",
    "ForestGreen": "#228b22",
}
// 刪除鍵為Coral的鍵值對
delete(myColors, "Coral")

// 顯示對映裡的所有顏色
for key, value := range myColors {
    fmt.Printf("Key: %s  Value: %s\n", key, value)
}

執行上面的程式碼,發現輸出的結果中已經沒有 Coral 了:

Key: DarkGray  Value: #a9a9a9
Key: ForestGreen  Value: #228b22
Key: AliceBlue  Value: #f0f8ff

在函式間傳遞對映

在函式間傳遞對映並不會製造出該對映的一個副本。實際上,當傳遞對映給一個函式,並對這個對映做了修改時,所有對這個對映的引用都會察覺到這個修改:

package main
import "fmt"

func main() {
    // 建立一個對映,儲存顏色以及顏色對應的十六進位制程式碼
    myColors := map[string]string{
        "AliceBlue":"#f0f8ff",
        "Coral":"#ff7F50",
        "DarkGray":"#a9a9a9",
        "ForestGreen": "#228b22",
    }
    // 顯示對映裡的所有顏色
    for key, value := range myColors {
        fmt.Printf("Key: %s  Value: %s\n", key, value)
    }
    fmt.Println()

    // 呼叫函式來移除指定的鍵
    removeColor(myColors, "Coral")
    // 顯示對映裡的所有顏色
    for key, value := range myColors {
        fmt.Printf("Key: %s Value: %s\n", key, value)
    }
}

// removeColor 將指定對映裡的鍵刪除
func removeColor(colors map[string]string, key string) {
    delete(colors, key)
}

執行上面的程式,輸出如下結果:

Key: Coral  Value: #ff7F50
Key: DarkGray  Value: #a9a9a9
Key: ForestGreen  Value: #228b22
Key: AliceBlue  Value: #f0f8ff

Key: AliceBlue Value: #f0f8ff
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22

可以看到,在呼叫了 removeColor 函式後,main 函式中引用的對映中也不再有 Coral 顏色了。這個特性和切片類似,保證可以用很小的成本來複制對映。

總結

對映是 Golang 中內建的儲存鍵值對型別資料的型別。從本文的示例中可以看出,Golang 對映實現的簡單易用,並且在函式間傳遞時的效能很好、開銷很低。