04.Map

JJJhr發表於2024-07-09

在 Go 語言中,map 是一種內建的資料結構,用於儲存鍵值對。map 提供了高效的鍵值對查詢、插入和刪除操作。下面是詳細介紹 map 的使用方法及注意事項。

1. 建立和初始化 map

使用 make 函式建立

可以使用 make 函式建立一個指定型別的 map

package main

import "fmt"

func main() {
    // 建立一個 key 為 string, value 為 int 的 map
    myMap := make(map[string]int)
    fmt.Println(myMap) // 輸出:map[]
}

使用字面量初始化

可以直接使用字面量初始化一個 map,同時設定初始值。

package main

import "fmt"

func main() {
    myMap := map[string]int{
        "one": 1,
        "two": 2,
        "three": 3,
    }
    fmt.Println(myMap) // 輸出:map[one:1 two:2 three:3]
}

在 Go 語言中,map 是無序的。每次遍歷或列印 map 中的鍵值對時,它們的順序可能不同。這個特性是由 Go 語言的內部實現決定的。即使你使用相同的程式碼,多次執行也可能得到不同的鍵值對順序。

具體原因如下:

  1. 內部雜湊實現map 的鍵是透過雜湊演算法計算得到的,而雜湊演算法的結果在記憶體佈局中並不保證順序。

  2. 最佳化效能:Go 語言的 map 實現為了最佳化效能和記憶體使用,沒有對鍵值對進行排序。因此,每次遍歷或列印時的順序可能不同。

2. 操作 map

新增和更新元素

使用 map[key] = value 的語法新增或更新 map 中的元素。

package main

import "fmt"

func main() {
    myMap := make(map[string]int)
    myMap["one"] = 1
    myMap["two"] = 2
    fmt.Println(myMap) // 輸出:map[one:1 two:2]

    // 更新元素
    myMap["one"] = 10
    fmt.Println(myMap) // 輸出:map[one:10 two:2]
}

訪問元素

使用 map[key] 的語法訪問 map 中的元素。可以使用兩個返回值的方式來判斷鍵是否存在。

package main

import "fmt"

func main() {
    myMap := map[string]int{"one": 1, "two": 2}
    
    value := myMap["one"]
    fmt.Println("Value for 'one':", value) // 輸出:Value for 'one': 1
    
    // 判斷鍵是否存在
    value, ok := myMap["three"]
    if ok {
        fmt.Println("Value for 'three':", value)
    } else {
        fmt.Println("'three' does not exist") // 輸出: 'three' does not exist
    }
}

刪除元素

使用 delete 函式從 map 中刪除元素。

package main

import "fmt"

func main() {
    myMap := map[string]int{"one": 1, "two": 2}
    
    delete(myMap, "one")
    fmt.Println(myMap) // 輸出:map[two:2]
}

3. 遍歷 map

使用 for range 語法遍歷 map 中的所有鍵值對。

package main

import "fmt"

func main() {
    myMap := map[string]int{"one": 1, "two": 2, "three": 3}
    
    for key, value := range myMap {
        fmt.Printf("Key: %s, Value: %d\n", key, value)
    }
}

4. 注意事項

map 是無序的

map 中的鍵值對是無序的,每次遍歷的順序可能不同。

零值

map 的零值是 nil,一個 nilmap 不能用於儲存鍵值對,否則會引發執行時錯誤。

package main

import "fmt"

func main() {
    var myMap map[string]int
    fmt.Println(myMap == nil) // 輸出:true
    
    // 下面的操作會引發執行時錯誤:panic: assignment to entry in nil map
    // myMap["one"] = 1
}

併發訪問

map 不是併發安全的。如果需要在多個 goroutine 中併發讀寫同一個 map,需要使用同步機制(如 sync.Mutexsync.RWMutex)來確保安全。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    myMap := make(map[string]int)
    var mu sync.Mutex

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            mu.Lock()
            myMap[fmt.Sprintf("key%d", i)] = i
            mu.Unlock()
        }(i)
    }

    wg.Wait()
    fmt.Println(myMap)
}

示例程式碼

下面是一個綜合使用 map 的示例程式碼:

package main

import "fmt"

func main() {
    // 建立並初始化 map
    myMap := map[string]int{"one": 1, "two": 2, "three": 3}

    // 新增和更新元素
    myMap["four"] = 4
    myMap["one"] = 10

    // 訪問元素
    value, ok := myMap["two"]
    if ok {
        fmt.Println("Value for 'two':", value)
    }

    // 刪除元素
    delete(myMap, "three")

    // 遍歷 map
    for key, value := range myMap {
        fmt.Printf("Key: %s, Value: %d\n", key, value)
    }
}