映射

映射 在 Go

2 個練習

關於 映射

在 Go 裡,map是一種內建的資料型態,代表雜湊表。在其他程式語言裡,你可能以dict、關聯式陣列或鍵值儲存的形式認識 map。如果你對這個概念不熟悉,可以把map想像成一本字典:每個單字是key,而解釋就是 map 的element。

開始之前,我想先請你參考 Go 規格書的 map 章節 和 Go 部落格的 map 文章,進一步深入了解map。

在語法上,map看起來像這樣:

map[KeyType]ElementType

KeyType必須是任何可比較的型別,而ElementType可以是 Go 中任何有效的型別,這表示你可以存放從基本的變數到 slice 的任何東西。

請務必記得,Go 裡的map是無序的。如果你試著走訪map並印出元素,可能會驚訝地發現元素是以隨機順序印出來的(有興趣的話不妨試試看)。

同樣重要的是要知道,每個鍵都是唯一的,這表示重複指定同一個鍵,會覆寫對應鍵的值。

map是參考型別,這表示當你把它四處傳遞時,Go 不會複製整份 map。相反地,Go 會複製 map 的指標,這讓把 map 傳給函式或變數的成本很低。未初始化的 map,其值為nil。

你可以像下面這樣定義 map(我們也稱它為nil map):

  var foo map[string]int

要初始化 map,你可以這樣做:

  // With map literal
  foo := map[string]int{}

或是

  // or with make function
  foo := make(map[string]int)

nil map 和已初始化的 map 不同,對 nil map 寫入會造成執行階段錯誤

panic: assignment to entry in nil map

因此,在使用 map 之前先初始化它很重要

以下是一些你可以對 map 進行的操作

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

如果你試著取得 map 中不存在的鍵所對應的值,它會回傳該值型別的零值。這可能會讓你困惑,尤其當你的ElementType的預設值(例如 int 的 0)本身就是一個有效值時。要檢查某個鍵是否存在於你的 map 中,你可以使用

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

如同前面看到的,要偵測map是否已初始化很簡單,只要把它和nil比較就好,但其他 map 又該怎麼辦呢?由於map不是可比較的型別,我們不能用==運算子,不過reflect套件有個東西可以救我們,它叫做DeepEqual,我們可以這樣用

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

但等等,如果 map 不是可比較的型別,為什麼我們能用nil跟它比較呢?這是因為規格書為此開了個例外,請參考可比較性規格

最後一點,如果你試著從多個 goroutine 寫入map,會觸發競態偵測器,請參考 Go 文件 和 The Go Blog。或者,你可以使用sync.Map、atomic或mutex來繞過這個問題。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 映射

練習已鎖定

再解鎖 3 個練習,就能練習 映射