美思 [Golang] 程式設計教學:使用映射 (Map)

Golang映射
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

在上一篇文章中,我們介紹了兩個線性的容器:陣列和切片,這兩個容器皆以數字為索引。在本文中,我們會介紹映射 (map),這是另外一種容器;映射儲存鍵/值 (key/value) 對,可以用數種資料型別做為鍵,取得相對應的值。

註:map 有些教材不翻,也有些教材翻成映射,本文取後者;相同的概念可見於 Python 的字典 (dictionary)、Perl 的雜湊 (hash) 和 PHP 的關連性陣列 (associative array) 等。

建立映射

以下實例建立一個以字串為鍵、以字串為值的映射,接著檢查某組鍵值對是否存在:

package main

import (
    "log"
)

func main() {
    // Declare an empty map.
    m := make(map[string]string)

    // Insert key/value pairs
    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    // Call the value by the key.
    if !(m["Go"] == "Beego") {
        log.Fatal("Wrong value")
    }

}

檢查鍵值對是否為空

如果鍵/值對不存在,會依映射的值的型別回傳預設值。在此例中,回傳空字串:

package main

import (
    "log"
)

func main() {
    m := make(map[string]string)

    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    // No such key/value pair.
    if !(m["Java"] == "") {
        log.Fatal("Wrong value")
    }
}

如果不確定鍵/值對是否存在,可以用以下語法檢查:

package main

import (
    "log"
)

func main() {
    m := make(map[string]string)

    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    v, ok := m["Go"]
    if !ok {
        log.Fatal("It should be true")
    }

    if !(v == "Beego") {
        log.Fatal("Wrong value")
    }

    _, ok = m["Java"]
    if !(ok == false) {
        log.Fatal("It should be false")
    }
}

移除鍵值對

我們也可以用 delete 函式去除鍵/值對:

package main

import (
    "log"
)

func main() {
    m := make(map[string]string)

    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    _, ok := m["PHP"]
    if !ok {
        log.Fatal("It should exist")
    }

    // Remove the key/value pair.
    delete(m, "PHP")

    _, ok = m["PHP"]
    if !(ok == false) {
        log.Fatal("It should not exist")
    }
}

映射的鍵/值對是單向的,我們僅能由鍵得到值,但不能由值得到鍵;另外,鍵不能重覆,但值可以。

走訪映射

類似於陣列和切片,我們也可以用迭代器走訪映射:

package main

import (
    "fmt"
)

func main() {
    m := make(map[string]string)

    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    for k, v := range m {
        fmt.Println(fmt.Sprintf("%s: %s", k, v))
    }
}

要注意的是,映射是無序的,我們多執行幾次,就會發現每次的順序都不一樣,見下例:

package main

import (
    "fmt"
)

func main() {
    m := make(map[string]string)

    m["Go"] = "Beego"
    m["Python"] = "Django"
    m["Ruby"] = "Rails"
    m["PHP"] = "Laravel"

    for i := 0; i < 10; i++ {
        for k, v := range m {
            fmt.Println(fmt.Sprintf("%s: %s", k, v))
        }

        fmt.Println("")
    }
}

如果想要保持鍵/值對的順序,我們要額外儲存鍵在一個切片中,見下例:

package main

import "fmt"

func main() {
    // Create an anomynous struct.
    data := struct {
        m     map[string]string
        order []string
    }{
        m:     make(map[string]string),
        order: []string{},
    }

    // Insert several key/value pairs.
    data.m["Go"] = "Beego"
    data.order = append(data.order, "Go")

    data.m["Python"] = "Django"
    data.order = append(data.order, "Python")

    data.m["Ruby"] = "Rails"
    data.order = append(data.order, "Ruby")

    data.m["PHP"] = "Laravel"
    data.order = append(data.order, "PHP")

    // Iterate these key/value pairs with order.
    for _, e := range data.order {
        fmt.Println(fmt.Sprintf("%s: %s", e, data.m[e]))
    }
}

在本例中,我們用一個結構體一併儲存映射和切片。我們會在後文介紹結構體。

結語

藉由本文的介紹,相信各位讀者可以知道映射如何使用。由於映射相當實用,各位讀者可以多多練習,以熟悉映射的使用方式。

關於作者

身為資訊領域碩士,美思認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

美思喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,美思將所學寫成文章,放在這個網站上和大家分享。