《Go語言四十二章經》第十三章 字典(Map)
《Go語言四十二章經》第十三章 字典(Map)
作者:李驍
13.1 字典(Map)
map 是引用型別,可以使用如下宣告:
var map1 map[keytype]valuetype var map1 map[string]int
([keytype] 和 valuetype 之間允許有空格,但是 Gofmt 移除了空格)
在宣告的時候不需要知道 map 的長度,map 是可以動態增長的。
未初始化的 map 的值是 nil。
key 可以是任意可以用 == 或者 != 操作符比較的型別,比如 string、int、float。所以陣列、切片和結構體不能作為 key (譯者注:含有陣列切片的結構體不能作為 key,只包含內建型別的 struct 是可以作為 key 的),但是指標和介面型別可以。如果要用結構體作為 key 可以提供 Key() 和 Hash() 方法,這樣可以通過結構體的域計算出唯一的數字或者字串的 key。
value 可以是任意型別的;通過使用空介面型別,我們可以儲存任意值,但是使用這種型別作為值時需要先做一次型別斷言。map 也可以用函式作為自己的值,這樣就可以用來做分支結構:key 用來選擇要執行的函式。
map 傳遞給函式的代價很小:在 32 位機器上佔 4 個位元組,64 位機器上佔 8 個位元組,無論實際上儲存了多少資料。
通過 key 在 map 中尋找值是很快的,比線性查詢快得多,但是仍然比從陣列和切片的索引中直接讀取要慢 100 倍;所以如果你很在乎效能的話還是建議用切片來解決問題。
map 可以用 {key1: val1, key2: val2} 的描述方法來初始化,就像陣列和結構體一樣。
map 是 引用型別 的: 記憶體用 make 方法來分配。
map 的初始化:
var map1 = make(map[keytype]valuetype)
map 容量: 和陣列不同,map 可以根據新增的 key-value 對動態的伸縮,因此它不存在固定長度或者最大限制。但是你也可以選擇標明 map 的初始容量 capacity,就像這樣:make(map[keytype]valuetype,cap)。
例如:
map2 := make(map[string]float32, 100)
當 map 增長到容量上限的時候,如果再增加新的 key-value 對,map 的大小會自動加 1。所以出於效能的考慮,對於大的 map 或者會快速擴張的 map,即使只是大概知道容量,也最好先標明。
在一個 nil 的slice中新增元素是沒問題的,但對一個map做同樣的事將會生成一個執行時的panic。
Works: package main func main() { var s []int s = append(s, 1) } Fails: package main func main() { var m map[string]int m["one"] = 1 // 錯誤 }
map的key訪問,val1, isPresent := map1[key1]或者 val1 = map1[key1] 的方法獲取 key1 對應的值 val1。
一般判斷是否某個key存在,不使用值判斷,而使用下面的方式:
if _, ok := x["two"]; !ok { fmt.Println("no entry") }
用切片作為 map 的值
既然一個 key 只能對應一個 value,而 value 又是一個原始型別,那麼如果一個 key 要對應多個值怎麼辦?例如,當我們要處理unix機器上的所有程序,以父程序(pid 為整形)作為 key,所有的子程序(以所有子程序的 pid 組成的切片)作為 value。通過將 value 定義為 []int 型別或者其他型別的切片,就可以優雅的解決這個問題。
這裡有一些定義 map 的例子:
// 宣告但未初始化map,此時是map的零值狀態 map1 := make(map[string]string, 5) map2 := make(map[string]string) // 建立了初始化了一個空的的map,這個時候沒有任何元素 map3 := map[string]string{} // map中有三個值 map4 := map[string]string{"a": "1", "b": "2", "c": "3"}
從 map1 中刪除 key1: 直接 delete(map1, key1) 就可以。如果 key1 不存在,該操作不會產生錯誤。
Delete(map4, "a")
map 預設是無序的,不管是按照 key 還是按照 value 預設都不排序。 如果你想為 map 排序,需要將 key(或者 value)拷貝到一個切片,再對切片排序(使用 sort 包)。
13.2 "range"語句中更新引用元素的值
在"range"語句中生成的資料的值是真實集合元素的拷貝。它們不是原有元素的引用。 這意味著更新這些值將不會修改原來的資料。同時也意味著使用這些值的地址將不會得到原有資料的指標。
package main import "fmt" func main() { data := []int{1, 2, 3} for _, v := range data { v *= 10 // 通常資料項不會改變 } fmt.Println("data:", data) // 程式輸出: [1 2 3] }
如果你需要更新原有集合中的資料,使用索引操作符來獲得資料。
package main import "fmt" func main() { data := []int{1, 2, 3} for i, _ := range data { data[i] *= 10 } fmt.Println("data:", data) // 程式輸出 data: [10 20 30] }
本書《Go語言四十二章經》內容在github上同步地址:https://github.com/ffhelicopter/Go42 本書《Go語言四十二章經》內容在簡書同步地址:https://www.jianshu.com/nb/29056963
雖然本書中例子都經過實際執行,但難免出現錯誤和不足之處,煩請您指出;如有建議也歡迎交流。 聯絡郵箱:[email protected]